From e833e7ad442f5b59f95c6ccfcaaf1d103d247d69 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Mon, 4 Jan 2010 11:50:04 -0600 Subject: CTS-54 Fix for SSAO artifacts far away. --- .../shaders/class1/deferred/sunLightF.glsl | 82 ++++++++++++---------- 1 file changed, 46 insertions(+), 36 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl index 22bdd2c7f3..fafc2ae3cc 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl @@ -53,51 +53,61 @@ vec4 getPosition(vec2 pos_screen) //calculate decreases in ambient lighting when crowded out (SSAO) float calcAmbientOcclusion(vec4 pos, vec3 norm) { - vec2 kern[8]; - // exponentially (^2) distant occlusion samples spread around origin - kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; - kern[1] = vec2(1.0, 0.0) * 0.250*0.250; - kern[2] = vec2(0.0, 1.0) * 0.375*0.375; - kern[3] = vec2(0.0, -1.0) * 0.500*0.500; - kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; - kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; - kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; - kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; - - vec2 pos_screen = vary_fragcoord.xy; - vec3 pos_world = pos.xyz; - vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; + float ret = 1.0; - float angle_hidden = 0.0; - int points = 0; + float dist = dot(pos.xyz,pos.xyz); - float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); - - // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?) - for (int i = 0; i < 8; i++) + if (dist < 64.0*64.0) { - vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect); - vec3 samppos_world = getPosition(samppos_screen).xyz; + vec2 kern[8]; + // exponentially (^2) distant occlusion samples spread around origin + kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; + kern[1] = vec2(1.0, 0.0) * 0.250*0.250; + kern[2] = vec2(0.0, 1.0) * 0.375*0.375; + kern[3] = vec2(0.0, -1.0) * 0.500*0.500; + kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; + kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; + kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; + kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; + + vec2 pos_screen = vary_fragcoord.xy; + vec3 pos_world = pos.xyz; + vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; - vec3 diff = pos_world - samppos_world; - float dist2 = dot(diff, diff); + float angle_hidden = 0.0; + int points = 0; - // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area - // --> solid angle shrinking by the square of distance - //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 - //(k should vary inversely with # of samples, but this is taken care of later) + float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); - //if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) // -0.05*norm to shift sample point back slightly for flat surfaces - // angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional. max of 1.0 (= ssao_factor_inv * ssao_factor) - angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); + // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?) + for (int i = 0; i < 8; i++) + { + vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect); + vec3 samppos_world = getPosition(samppos_screen).xyz; + + vec3 diff = pos_world - samppos_world; + float dist2 = dot(diff, diff); + + // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area + // --> solid angle shrinking by the square of distance + //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 + //(k should vary inversely with # of samples, but this is taken care of later) + + //if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) // -0.05*norm to shift sample point back slightly for flat surfaces + // angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional. max of 1.0 (= ssao_factor_inv * ssao_factor) + angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); + + // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" + points = points + int(diff.z > -1.0); + } - // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" - points = points + int(diff.z > -1.0); + angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); + + ret = (1.0 - (float(points != 0) * angle_hidden)); + ret += max((dist-32.0*32.0)/(32.0*32.0), 0.0); } - angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); - - return (1.0 - (float(points != 0) * angle_hidden)); + return min(ret, 1.0); } void main() -- cgit v1.3 From 728463b40b9794b9fa4cd5b7bad3f169033d0bc8 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 8 Jan 2010 13:31:04 -0600 Subject: Cleaned up visible edges of projector frustum in diffuse/specular/ambient lighting from projectors. --- .../shaders/class1/deferred/multiSpotLightF.glsl | 102 +++++++++++++++------ .../shaders/class2/deferred/multiSpotLightF.glsl | 58 +++++++++++- .../shaders/class2/deferred/spotLightF.glsl | 11 --- 3 files changed, 128 insertions(+), 43 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl index 28bcd720c0..2c8d90d1a6 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl @@ -5,7 +5,6 @@ * $License$ */ - #version 120 #extension GL_ARB_texture_rectangle : enable @@ -15,6 +14,7 @@ uniform sampler2DRect specularRect; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; uniform samplerCube environmentMap; +uniform sampler2DRect lightMap; uniform sampler2D noiseMap; uniform sampler2D lightFunc; uniform sampler2D projectionMap; @@ -26,12 +26,15 @@ uniform vec3 proj_n; uniform float proj_focus; //distance from plane to begin blurring uniform float proj_lod; //(number of mips in proj map) uniform float proj_range; //range between near clip and far clip plane of projection +uniform float proj_ambient_lod; uniform float proj_ambiance; uniform float near_clip; uniform float far_clip; uniform vec3 proj_origin; //origin of projection to be used for angular attenuation uniform float sun_wash; +uniform int proj_shadow_idx; +uniform float shadow_fade; varying vec4 vary_light; @@ -40,6 +43,52 @@ uniform vec2 screen_res; uniform mat4 inv_proj; +vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float det = max(1.0-lod/(proj_lod*0.5), 0.0); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0); + + return ret; +} + +vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = vec2(0.5) - abs(tc-vec2(0.5)); + + float det = min(lod/(proj_lod*0.5), 1.0); + + float d = min(dist.x, dist.y); + + float edge = 0.25*det; + + ret *= clamp(d/edge, 0.0, 1.0); + + return ret; +} + +vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0); + + return ret; +} + + vec4 getPosition(vec2 pos_screen) { float depth = texture2DRect(depthMap, pos_screen.xy).a; @@ -68,7 +117,7 @@ void main() { discard; } - + vec3 norm = texture2DRect(normalMap, frag.xy).xyz*2.0-1.0; norm = normalize(norm); @@ -83,7 +132,11 @@ void main() proj_tc.xyz /= proj_tc.w; float fa = gl_Color.a+1.0; - float dist_atten = clamp(1.0-(dist2-1.0*(1.0-fa))/fa, 0.0, 1.0); + float dist_atten = min(1.0-(dist2-1.0*(1.0-fa))/fa, 1.0); + if (dist_atten <= 0.0) + { + discard; + } lv = proj_origin-pos.xyz; lv = normalize(lv); @@ -101,32 +154,32 @@ void main() proj_tc.y > 0.0) { float lit = 0.0; + float amb_da = proj_ambiance; + if (da > 0.0) { float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0); float lod = diff * proj_lod; - vec4 plcol = texture2DLod(projectionMap, proj_tc.xy, lod); + vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod); vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a; lit = da * dist_atten * noise; col = lcol*lit*diff_tex; + amb_da += (da*0.5)*proj_ambiance; } - float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0); - float lod = diff * proj_lod; - vec4 amb_plcol = texture2DLod(projectionMap, proj_tc.xy, lod); - //float amb_da = mix(proj_ambiance, proj_ambiance*max(-da, 0.0), max(da, 0.0)); - float amb_da = proj_ambiance; - + //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0); + vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod); + amb_da += (da*da*0.5+0.5)*proj_ambiance; - + amb_da *= dist_atten * noise; - + amb_da = min(amb_da, 1.0-lit); - + col += amb_da*gl_Color.rgb*diff_tex.rgb*amb_plcol.rgb*amb_plcol.a; } @@ -144,35 +197,28 @@ void main() { vec3 pfinal = pos + ref * dot(pdelta, proj_n)/ds; - vec3 stc = (proj_mat * vec4(pfinal.xyz, 1.0)).xyz; + vec4 stc = (proj_mat * vec4(pfinal.xyz, 1.0)); if (stc.z > 0.0) { - stc.xy /= stc.z+proj_near; - + stc.xy /= stc.w; + + float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0); + + stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5); + if (stc.x < 1.0 && stc.y < 1.0 && stc.x > 0.0 && stc.y > 0.0) { - vec4 scol = texture2DLod(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); + vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb; } } } } - /*if (spec.a > 0.0) - { - //vec3 ref = reflect(normalize(pos), norm); - float sa = dot(normalize(lv-normalize(pos)),norm);; - //sa = max(sa, 0.0); - //sa = pow(sa, 128.0 * spec.a*spec.a/dist_atten)*min(dist_atten*4.0, 1.0); - sa = texture2D(lightFunc, vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0); - sa *= noise; - col += da*sa*lcol*spec.rgb; - }*/ - gl_FragColor.rgb = col; gl_FragColor.a = 0.0; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl index 45884d5732..3e29c684e8 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl @@ -43,6 +43,52 @@ uniform vec2 screen_res; uniform mat4 inv_proj; +vec4 texture2DLodSpecular(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float det = max(1.0-lod/(proj_lod*0.5), 0.0); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0)+det, 1.0); + + return ret; +} + +vec4 texture2DLodDiffuse(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = vec2(0.5) - abs(tc-vec2(0.5)); + + float det = min(lod/(proj_lod*0.5), 1.0); + + float d = min(dist.x, dist.y); + + float edge = 0.25*det; + + ret *= clamp(d/edge, 0.0, 1.0); + + return ret; +} + +vec4 texture2DLodAmbient(sampler2D projectionMap, vec2 tc, float lod) +{ + vec4 ret = texture2DLod(projectionMap, tc, lod); + + vec2 dist = tc-vec2(0.5); + + float d = dot(dist,dist); + + ret *= min(clamp((0.25-d)/0.25, 0.0, 1.0), 1.0); + + return ret; +} + + vec4 getPosition(vec2 pos_screen) { float depth = texture2DRect(depthMap, pos_screen.xy).a; @@ -126,7 +172,7 @@ void main() float diff = clamp((l_dist-proj_focus)/proj_range, 0.0, 1.0); float lod = diff * proj_lod; - vec4 plcol = texture2DLod(projectionMap, proj_tc.xy, lod); + vec4 plcol = texture2DLodDiffuse(projectionMap, proj_tc.xy, lod); vec3 lcol = gl_Color.rgb * plcol.rgb * plcol.a; @@ -137,7 +183,7 @@ void main() } //float diff = clamp((proj_range-proj_focus)/proj_range, 0.0, 1.0); - vec4 amb_plcol = texture2DLod(projectionMap, proj_tc.xy, proj_lod); + vec4 amb_plcol = texture2DLodAmbient(projectionMap, proj_tc.xy, proj_lod); amb_da += (da*da*0.5+0.5)*proj_ambiance; @@ -167,13 +213,17 @@ void main() if (stc.z > 0.0) { stc.xy /= stc.w; - + + float fatten = clamp(spec.a*spec.a+spec.a*0.5, 0.25, 1.0); + + stc.xy = (stc.xy - vec2(0.5)) * fatten + vec2(0.5); + if (stc.x < 1.0 && stc.y < 1.0 && stc.x > 0.0 && stc.y > 0.0) { - vec4 scol = texture2DLod(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); + vec4 scol = texture2DLodSpecular(projectionMap, stc.xy, proj_lod-spec.a*proj_lod); col += dist_atten*scol.rgb*gl_Color.rgb*scol.a*spec.rgb*shadow; } } diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl index d6534083cf..6617e33c0d 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl @@ -179,17 +179,6 @@ void main() } } - /*if (spec.a > 0.0) - { - //vec3 ref = reflect(normalize(pos), norm); - float sa = dot(normalize(lv-normalize(pos)),norm);; - //sa = max(sa, 0.0); - //sa = pow(sa, 128.0 * spec.a*spec.a/dist_atten)*min(dist_atten*4.0, 1.0); - sa = texture2D(lightFunc, vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0); - sa *= noise; - col += da*sa*lcol*spec.rgb; - }*/ - //attenuate point light contribution by SSAO component col *= texture2DRect(lightMap, frag.xy).g; -- cgit v1.3 From 73c98e1c1e3899aaae1de6bac51f9e21ea6432a1 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Thu, 28 Jan 2010 18:01:12 -0800 Subject: enable basic directional lighting for basic/atmospheric shaders - yay. next, needs the non-shader GL lighting model to be equally dumb (it's too smart - kill the spot angle. --- .../shaders/class1/lighting/lightFuncV.glsl | 21 +++++++++++++++++++++ .../shaders/class2/lighting/sumLightsV.glsl | 8 +++++--- 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index 3e8fdfb3e4..211de830fa 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -28,7 +28,28 @@ float calcPointLight(vec3 v, vec3 n, vec4 lp, float la) //angular attenuation da *= calcDirectionalLight(n, lv); + + return da; +} + + +float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la) +{ + //get light vector + vec3 lv = lp.xyz-v; + //get distance + float d = length(lv); + + //normalize light vector + lv *= 1.0/d; + + //distance attenuation + float da = clamp(1.0/(la * d), 0.0, 1.0); + + //angular attenuation + da *= dot(lv, -ln) * calcDirectionalLight(n, lv); + return da; } diff --git a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl index f4c59734a4..fd264b9e74 100644 --- a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl +++ b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl @@ -7,6 +7,7 @@ float calcDirectionalLight(vec3 n, vec3 l); float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); +float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); @@ -18,9 +19,10 @@ vec4 sumLights(vec3 pos, vec3 norm, vec4 color, vec4 baseLight) // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[1].diffuse.rgb * calcDirectionalLight(norm, gl_LightSource[1].position.xyz); - col.rgb += gl_LightSource[2].diffuse.rgb * calcPointLight(pos, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation); - col.rgb += gl_LightSource[3].diffuse.rgb * calcPointLight(pos, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation); - //col.rgb += gl_LightSource[4].diffuse.rgb * calcPointLight(pos, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation); + + col.rgb += gl_LightSource[2].diffuse.rgb * calcPointLight2(pos, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation); + col.rgb += gl_LightSource[3].diffuse.rgb * calcPointLight2(pos, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation); + //col.rgb += gl_LightSource[4].diffuse.rgb * calcPointLight2(pos, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation); col.rgb = scaleDownLight(col.rgb); // Add windlight lights -- cgit v1.3 From c413a0e096f8897ddc4ee5b5a627e6bf6cce39bb Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Thu, 28 Jan 2010 18:36:04 -0800 Subject: fix the shader dp light going negative! --- indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index 211de830fa..0f03e336be 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -48,7 +48,7 @@ float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la) float da = clamp(1.0/(la * d), 0.0, 1.0); //angular attenuation - da *= dot(lv, -ln) * calcDirectionalLight(n, lv); + da *= calcDirectionalLight(-ln, lv) * calcDirectionalLight(n, lv); return da; } -- cgit v1.3 From f773b6ef9669bab6765678f8b07831144dd44a5e Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Thu, 28 Jan 2010 18:50:10 -0800 Subject: cheaply resurrect the difference between spotlight and omnidirectional, as far as the shader is concerned. not complete. --- indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl | 4 ++-- indra/newview/pipeline.cpp | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index 0f03e336be..b2d6d23b1e 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -33,7 +33,7 @@ float calcPointLight(vec3 v, vec3 n, vec4 lp, float la) } -float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la) +float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_omnidirectional) { //get light vector vec3 lv = lp.xyz-v; @@ -48,7 +48,7 @@ float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la) float da = clamp(1.0/(la * d), 0.0, 1.0); //angular attenuation - da *= calcDirectionalLight(-ln, lv) * calcDirectionalLight(n, lv); + da *= clamp(dot(-ln, lv)+is_omnidirectional, 0.0, 1.0) * calcDirectionalLight(n, lv); return da; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 71f9ce7eb2..6cd8b94405 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -4592,7 +4592,6 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) glLightfv(gllight, GL_POSITION, light_pos_gl.mV); glLightfv(gllight, GL_DIFFUSE, light_color.mV); glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); - glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); glLightf (gllight, GL_LINEAR_ATTENUATION, atten); glLightf (gllight, GL_QUADRATIC_ATTENUATION, quad); @@ -4606,11 +4605,16 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) glLightfv(gllight, GL_SPOT_DIRECTION, at_axis.mV); glLightf (gllight, GL_SPOT_EXPONENT, 1.0f); // 1.0 = good old dot product glLightf (gllight, GL_SPOT_CUTOFF, 90.0f); // hemisphere + glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); } else // omnidirectional (point) light { glLightf (gllight, GL_SPOT_EXPONENT, 0.0f); glLightf (gllight, GL_SPOT_CUTOFF, 180.0f); + + // we use specular.w = 1.0 as a cheap hack for the shaders to know that this is omnidirectional rather than a spotlight + const float specular = {0.f, 0.f, 0.f, 1.f}, + glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); } cur_light++; if (cur_light >= 8) -- cgit v1.3 From 1237c123873b98f2987d8d9fb573f6f88f8eebc2 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Thu, 28 Jan 2010 21:49:31 -0800 Subject: cheaper, cuter way to deal with spot vs point in the shader --- indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index b2d6d23b1e..4525bf80b2 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -48,7 +48,7 @@ float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_omnid float da = clamp(1.0/(la * d), 0.0, 1.0); //angular attenuation - da *= clamp(dot(-ln, lv)+is_omnidirectional, 0.0, 1.0) * calcDirectionalLight(n, lv); + da *= max(dot(-ln, lv),is_omnidirectional) * calcDirectionalLight(n, lv); return da; } -- cgit v1.3 From fcaca49a9496154419a8f44c17cba16b028d3518 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Thu, 28 Jan 2010 23:58:50 -0800 Subject: cheap no-branch goodness for rendering both spotlights and pointlights with the same shader code. kill already-nerfed quadratic attenuation which it seems we won't be supporting any decade soon. --- .../shaders/class1/lighting/lightFuncV.glsl | 6 +++- .../shaders/class2/lighting/sumLightsV.glsl | 2 +- indra/newview/pipeline.cpp | 42 ++++++++-------------- 3 files changed, 21 insertions(+), 29 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index 4525bf80b2..2e41f24afe 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -47,8 +47,12 @@ float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_omnid //distance attenuation float da = clamp(1.0/(la * d), 0.0, 1.0); + // spotlight coefficient. + float spot = max(dot(-ln, lv), is_omnidirectional); + da *= spot*spot; // GL_SPOT_EXPONENT=2 + //angular attenuation - da *= max(dot(-ln, lv),is_omnidirectional) * calcDirectionalLight(n, lv); + da *= calcDirectionalLight(n, lv); return da; } diff --git a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl index e5bf76db55..4577b4be55 100644 --- a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl +++ b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl @@ -7,7 +7,7 @@ float calcDirectionalLight(vec3 n, vec3 l); float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); -float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la); +float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_omnidirectional); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index e12412c58c..2ad0bad11f 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -4572,40 +4572,30 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) LLVector4 light_pos_gl(light_pos, 1.0f); F32 light_radius = llmax(light->getLightRadius(), 0.001f); - F32 atten, quad; -#if 0 //1.9.1 - if (pool->getVertexShaderLevel() > 0) - { - atten = light_radius; - quad = llmax(light->getLightFalloff(), 0.0001f); - } - else -#endif - { - F32 x = (3.f * (1.f + light->getLightFalloff())); - atten = x / (light_radius); // % of brightness at radius - quad = 0.0f; - } + F32 x = (3.f * (1.f + light->getLightFalloff())); + float linatten = x / (light_radius); // % of brightness at radius + mHWLightColors[cur_light] = light_color; S32 gllight = GL_LIGHT0+cur_light; glLightfv(gllight, GL_POSITION, light_pos_gl.mV); glLightfv(gllight, GL_DIFFUSE, light_color.mV); glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); - glLightf (gllight, GL_LINEAR_ATTENUATION, atten); - glLightf (gllight, GL_QUADRATIC_ATTENUATION, quad); + glLightf (gllight, GL_LINEAR_ATTENUATION, linatten); + glLightf (gllight, GL_QUADRATIC_ATTENUATION, 0.0f); if (light->getLightTexture()) // directional (spot-)light { LLVector3 spotparams = light->getSpotLightParams(); LLQuaternion quat = light->getRenderRotation(); LLVector3 at_axis(0,0,-1); // this matches deferred rendering's object light direction at_axis *= quat; - llinfos << "SPOT!!!!!!! fov: " << spotparams.mV[0] << " focus: " << spotparams.mV[1] << " dir: " << at_axis << llendl; + //llinfos << "SPOT!!!!!!! fov: " << spotparams.mV[0] << " focus: " << spotparams.mV[1] << " dir: " << at_axis << llendl; glLightfv(gllight, GL_SPOT_DIRECTION, at_axis.mV); - glLightf (gllight, GL_SPOT_EXPONENT, 1.0f); // 1.0 = good old dot product + glLightf (gllight, GL_SPOT_EXPONENT, 2.0f); // 2.0 = good old dot product ^ 2 glLightf (gllight, GL_SPOT_CUTOFF, 90.0f); // hemisphere - glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); + const float specular[] = {0.f, 0.f, 0.f, 0.f}; + glLightfv(gllight, GL_SPECULAR, specular); } else // omnidirectional (point) light { @@ -4615,6 +4605,7 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) // we use specular.w = 1.0 as a cheap hack for the shaders to know that this is omnidirectional rather than a spotlight const float specular[] = {0.f, 0.f, 0.f, 1.f}; glLightfv(gllight, GL_SPECULAR, specular); + //llinfos << "boring light" << llendl; } cur_light++; if (cur_light >= 8) @@ -4641,13 +4632,10 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) LLVector4 light_pos_gl(light_pos, 1.0f); F32 light_radius = 16.f; - F32 atten, quad; - { - F32 x = 3.f; - atten = x / (light_radius); // % of brightness at radius - quad = 0.0f; - } + F32 x = 3.f; + float linatten = x / (light_radius); // % of brightness at radius + mHWLightColors[2] = light_color; S32 gllight = GL_LIGHT2; glLightfv(gllight, GL_POSITION, light_pos_gl.mV); @@ -4655,8 +4643,8 @@ void LLPipeline::setupHWLights(LLDrawPool* pool) glLightfv(gllight, GL_AMBIENT, LLColor4::black.mV); glLightfv(gllight, GL_SPECULAR, LLColor4::black.mV); glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f); - glLightf (gllight, GL_LINEAR_ATTENUATION, atten); - glLightf (gllight, GL_QUADRATIC_ATTENUATION, quad); + glLightf (gllight, GL_LINEAR_ATTENUATION, linatten); + glLightf (gllight, GL_QUADRATIC_ATTENUATION, 0.0f); glLightf (gllight, GL_SPOT_EXPONENT, 0.0f); glLightf (gllight, GL_SPOT_CUTOFF, 180.0f); } -- cgit v1.3 From d0cf5b03f4fec29db367d8f1f387f2b0996f67e6 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 29 Jan 2010 00:22:13 -0800 Subject: spot-aware light func usage in ALL shaders. --- .../shaders/class1/deferred/alphaV.glsl | 14 +++++++------- .../shaders/class1/deferred/avatarAlphaV.glsl | 14 +++++++------- .../shaders/class1/lighting/lightFuncV.glsl | 22 +--------------------- .../shaders/class2/deferred/alphaV.glsl | 14 +++++++------- .../shaders/class2/deferred/avatarAlphaV.glsl | 14 +++++++------- .../shaders/class2/lighting/sumLightsV.glsl | 9 ++++----- .../shaders/class3/lighting/sumLightsV.glsl | 15 ++++++++------- 7 files changed, 41 insertions(+), 61 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl index 1a7d58b07b..91793a29d3 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl @@ -9,7 +9,7 @@ vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); void calcAtmospherics(vec3 inPositionEye); float calcDirectionalLight(vec3 n, vec3 l); -float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); @@ -49,12 +49,12 @@ void main() col.rgb = scaleUpLight(col.rgb); // Collect normal lights (need to be divided by two, as we later multiply by 2) - col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation); - col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation); - col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation); - col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].linearAttenuation); - col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].linearAttenuation); - col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].linearAttenuation); + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl index c1988d3c78..e36564df10 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl @@ -10,7 +10,7 @@ mat4 getSkinnedTransform(); void calcAtmospherics(vec3 inPositionEye); float calcDirectionalLight(vec3 n, vec3 l); -float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); @@ -55,12 +55,12 @@ void main() col.rgb = scaleUpLight(col.rgb); // Collect normal lights (need to be divided by two, as we later multiply by 2) - col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation); - col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation); - col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation); - col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].linearAttenuation); - col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].linearAttenuation); - col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].linearAttenuation); + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index 2e41f24afe..d0f58cc1a4 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -12,28 +12,8 @@ float calcDirectionalLight(vec3 n, vec3 l) return a; } -float calcPointLight(vec3 v, vec3 n, vec4 lp, float la) -{ - //get light vector - vec3 lv = lp.xyz-v; - - //get distance - float d = length(lv); - - //normalize light vector - lv *= 1.0/d; - - //distance attenuation - float da = clamp(1.0/(la * d), 0.0, 1.0); - - //angular attenuation - da *= calcDirectionalLight(n, lv); - - return da; -} - -float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_omnidirectional) +float calcPointlightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight) { //get light vector vec3 lv = lp.xyz-v; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl index 5991e1f3b5..eec655e3df 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl @@ -9,7 +9,7 @@ vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); void calcAtmospherics(vec3 inPositionEye); float calcDirectionalLight(vec3 n, vec3 l); -float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); @@ -50,12 +50,12 @@ void main() col.rgb = scaleUpLight(col.rgb); // Collect normal lights (need to be divided by two, as we later multiply by 2) - col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation); - col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation); - col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation); - col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].linearAttenuation); - col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].linearAttenuation); - col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].linearAttenuation); + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); diff --git a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl index a939499b17..488bd677e1 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl @@ -10,7 +10,7 @@ mat4 getSkinnedTransform(); void calcAtmospherics(vec3 inPositionEye); float calcDirectionalLight(vec3 n, vec3 l); -float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); @@ -61,12 +61,12 @@ void main() col.rgb = scaleUpLight(col.rgb); // Collect normal lights (need to be divided by two, as we later multiply by 2) - col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation); - col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation); - col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation); - col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].linearAttenuation); - col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].linearAttenuation); - col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].linearAttenuation); + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); diff --git a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl index 4577b4be55..19800d96dc 100644 --- a/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl +++ b/indra/newview/app_settings/shaders/class2/lighting/sumLightsV.glsl @@ -6,8 +6,7 @@ */ float calcDirectionalLight(vec3 n, vec3 l); -float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); -float calcPointLight2(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_omnidirectional); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); @@ -20,9 +19,9 @@ vec4 sumLights(vec3 pos, vec3 norm, vec4 color, vec4 baseLight) // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[1].diffuse.rgb * calcDirectionalLight(norm, gl_LightSource[1].position.xyz); - col.rgb += gl_LightSource[2].diffuse.rgb * calcPointLight2(pos, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); - col.rgb += gl_LightSource[3].diffuse.rgb * calcPointLight2(pos, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); - //col.rgb += gl_LightSource[4].diffuse.rgb * calcPointLight2(pos, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[2].diffuse.rgb * calcPointLightOrSpotLight(pos, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb * calcPointLightOrSpotLight(pos, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + //col.rgb += gl_LightSource[4].diffuse.rgb * calcPointLightOrSpotLight(pos, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); col.rgb = scaleDownLight(col.rgb); // Add windlight lights diff --git a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl index 1c5234c450..0fdfdf2dde 100644 --- a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl +++ b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl @@ -6,7 +6,7 @@ */ float calcDirectionalLight(vec3 n, vec3 l); -float calcPointLight(vec3 v, vec3 n, vec4 lp, float la); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); vec3 atmosAmbient(vec3 light); vec3 atmosAffectDirectionalLight(float lightIntensity); @@ -24,12 +24,13 @@ vec4 sumLights(vec3 pos, vec3 norm, vec4 color, vec4 baseLight) col.rgb = scaleUpLight(col.rgb); // Collect normal lights (need to be divided by two, as we later multiply by 2) - col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation); - col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation); - col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation); - col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[5].position, gl_LightSource[5].linearAttenuation); - col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[6].position, gl_LightSource[6].linearAttenuation); - col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[7].position, gl_LightSource[7].linearAttenuation); + + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); -- cgit v1.3 From 71b35bd63daecfe6ca60560a13f72990afae81ac Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 29 Jan 2010 00:31:39 -0800 Subject: Don't scale windlight lights up just to scale them down again later. avoid this with rearrangement. not tested. --- .../app_settings/shaders/class1/deferred/alphaV.glsl | 10 ++++------ .../app_settings/shaders/class1/deferred/avatarAlphaV.glsl | 10 ++++------ .../app_settings/shaders/class2/deferred/alphaV.glsl | 10 ++++------ .../app_settings/shaders/class2/deferred/avatarAlphaV.glsl | 10 ++++------ .../app_settings/shaders/class3/lighting/sumLightsV.glsl | 14 +++++--------- 5 files changed, 21 insertions(+), 33 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl index 91793a29d3..cb0d7b797b 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl @@ -41,12 +41,7 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col; - col.a = gl_Color.a; - - // Add windlight lights - col.rgb = atmosAmbient(vec3(0.)); - col.rgb = scaleUpLight(col.rgb); + vec4 col(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); @@ -58,6 +53,9 @@ void main() col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); + // Add windlight lights + col.rgb += atmosAmbient(vec3(0.)); + vary_light = gl_LightSource[0].position.xyz; vary_ambient = col.rgb*gl_Color.rgb; diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl index e36564df10..e34f6ba552 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl @@ -47,12 +47,7 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col; - col.a = gl_Color.a; - - // Add windlight lights - col.rgb = atmosAmbient(vec3(0.)); - col.rgb = scaleUpLight(col.rgb); + vec4 col(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); @@ -64,6 +59,9 @@ void main() col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); + // Add windlight lights + col.rgb += atmosAmbient(vec3(0.)); + vary_ambient = col.rgb*gl_Color.rgb; vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a))); diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl index eec655e3df..05a32578c1 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl @@ -42,12 +42,7 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col; - col.a = gl_Color.a; - - // Add windlight lights - col.rgb = atmosAmbient(vec3(0.)); - col.rgb = scaleUpLight(col.rgb); + vec4 col(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); @@ -59,6 +54,9 @@ void main() col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); + // Add windlight lights + col.rgb += atmosAmbient(vec3(0.)); + vary_light = gl_LightSource[0].position.xyz; vary_ambient = col.rgb*gl_Color.rgb; diff --git a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl index 488bd677e1..d63baeb913 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl @@ -53,12 +53,7 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col; - col.a = gl_Color.a; - - // Add windlight lights - col.rgb = atmosAmbient(vec3(0.)); - col.rgb = scaleUpLight(col.rgb); + vec4 col(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); @@ -70,6 +65,9 @@ void main() col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); + // Add windlight lights + col.rgb += atmosAmbient(vec3(0.)); + vary_ambient = col.rgb*gl_Color.rgb; vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a))); diff --git a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl index 0fdfdf2dde..371d6b526c 100644 --- a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl +++ b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl @@ -15,16 +15,9 @@ vec3 scaleUpLight(vec3 light); vec4 sumLights(vec3 pos, vec3 norm, vec4 color, vec4 baseLight) { - vec4 col; - col.a = color.a; + vec4 col(0.0, 0.0, 0.0, color.a); - // Add windlight lights - col.rgb = atmosAffectDirectionalLight(calcDirectionalLight(norm, gl_LightSource[0].position.xyz)); - col.rgb += atmosAmbient(baseLight.rgb); - col.rgb = scaleUpLight(col.rgb); - // Collect normal lights (need to be divided by two, as we later multiply by 2) - col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); @@ -33,7 +26,10 @@ vec4 sumLights(vec3 pos, vec3 norm, vec4 color, vec4 baseLight) col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); col.rgb = scaleDownLight(col.rgb); - + + // Add windlight lights + col.rgb += atmosAffectDirectionalLight(calcDirectionalLight(norm, gl_LightSource[0].position.xyz)); + col.rgb += atmosAmbient(baseLight.rgb); col.rgb = min(col.rgb*color.rgb, 1.0); -- cgit v1.3 From 6df5cd7a18187c83b84915c8fa0d4c32f1282264 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 29 Jan 2010 00:57:28 -0800 Subject: duh, use the proper param name. --- indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index d0f58cc1a4..2b7e8b125b 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -28,7 +28,7 @@ float calcPointlightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, floa float da = clamp(1.0/(la * d), 0.0, 1.0); // spotlight coefficient. - float spot = max(dot(-ln, lv), is_omnidirectional); + float spot = max(dot(-ln, lv), is_pointlight); da *= spot*spot; // GL_SPOT_EXPONENT=2 //angular attenuation -- cgit v1.3 From aaae230bb135b9e57edb12a7374c711fd7e4adb4 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 29 Jan 2010 01:45:13 -0800 Subject: fix a variety of shader errors, mostly due to my confusing glsl with C++... again --- indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl | 3 ++- indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl | 3 ++- indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl | 3 ++- indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl index cb0d7b797b..04e556c11a 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl @@ -41,7 +41,8 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col(0.0, 0.0, 0.0, gl_Color.a); + + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl index e34f6ba552..650fbcc3f5 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl @@ -47,7 +47,8 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col(0.0, 0.0, 0.0, gl_Color.a); + + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl index 2b7e8b125b..da49e59b89 100644 --- a/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFuncV.glsl @@ -13,7 +13,7 @@ float calcDirectionalLight(vec3 n, vec3 l) } -float calcPointlightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight) +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight) { //get light vector vec3 lv = lp.xyz-v; diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl index 05a32578c1..1fae8c4da3 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaV.glsl @@ -42,7 +42,7 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col(0.0, 0.0, 0.0, gl_Color.a); + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); diff --git a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl index d63baeb913..f8dd1b7431 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/avatarAlphaV.glsl @@ -53,7 +53,8 @@ void main() calcAtmospherics(pos.xyz); //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); - vec4 col(0.0, 0.0, 0.0, gl_Color.a); + + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); diff --git a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl index 371d6b526c..f129a1517b 100644 --- a/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl +++ b/indra/newview/app_settings/shaders/class3/lighting/sumLightsV.glsl @@ -15,7 +15,7 @@ vec3 scaleUpLight(vec3 light); vec4 sumLights(vec3 pos, vec3 norm, vec4 color, vec4 baseLight) { - vec4 col(0.0, 0.0, 0.0, color.a); + vec4 col = vec4(0.0, 0.0, 0.0, color.a); // Collect normal lights (need to be divided by two, as we later multiply by 2) col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); -- cgit v1.3 From 665567e2f97f8ec4de2810fb63d6b845e67833e2 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Sun, 7 Mar 2010 09:40:11 +0000 Subject: Remove the class2/class3 water shaders, which aren't materially different from class1 except for some minor const tweaks (I've migrated class3's tweaks into class1 on the assumption that this was what davep sees all the time) --- indra/newview/app_settings/shaders/class1/deferred/waterF.glsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index d21575119d..5cb8bbba0f 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -131,11 +131,11 @@ void main() //color.rgb = scaleSoftClip(color.rgb); //color.a = spec * sunAngle2; - //wavef.z = -0.25f; + //wavef.z *= 0.1f; wavef = normalize(wavef); wavef = (norm_mat*vec4(wavef, 1.0)).xyz; - gl_FragData[0] = vec4(color.rgb, 0.75); - gl_FragData[1] = vec4(1,1,1, 0.8); + gl_FragData[0] = vec4(color.rgb, 0.5); + gl_FragData[1] = vec4(0.5,0.5,0.5, 0.95); gl_FragData[2] = vec4(wavef*0.5+0.5, 0.0); } -- cgit v1.3 From bd884d89e9d278454f0c9f27d5083a9d6500d582 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Sun, 7 Mar 2010 09:41:47 +0000 Subject: Remove an extraneous normalize() from the water fragment shader - the last thing we did to this vector was normalize it anyway. --- indra/newview/app_settings/shaders/class1/deferred/waterF.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index 5cb8bbba0f..411c4bc1c3 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -132,7 +132,7 @@ void main() //color.a = spec * sunAngle2; //wavef.z *= 0.1f; - wavef = normalize(wavef); + //wavef = normalize(wavef); wavef = (norm_mat*vec4(wavef, 1.0)).xyz; gl_FragData[0] = vec4(color.rgb, 0.5); -- cgit v1.3 From ead28366c6f33ca2794043ffbced28a950e6e334 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Sun, 7 Mar 2010 10:09:06 +0000 Subject: trivial comment typo fix. --- indra/newview/app_settings/shaders/class1/deferred/waterF.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index 411c4bc1c3..1fc5811382 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -115,7 +115,7 @@ void main() vec4 fb = texture2D(screenTex, distort2); //mix with reflection - // Note we actually want to use just df1, but multiplying by 0.999999 gets around and nvidia compiler bug + // Note we actually want to use just df1, but multiplying by 0.999999 gets around an nvidia compiler bug color.rgb = mix(fb.rgb, refcol.rgb, df1 * 0.99999); float shadow = 1.0; -- cgit v1.3 From 88a1b6676ed825345f0615050b9870b325fb6ef1 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Sun, 7 Mar 2010 10:15:07 +0000 Subject: Some extra comments to help me get things straight. --- indra/newview/app_settings/shaders/class1/deferred/waterF.glsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index 1fc5811382..55c893d73b 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -135,7 +135,7 @@ void main() //wavef = normalize(wavef); wavef = (norm_mat*vec4(wavef, 1.0)).xyz; - gl_FragData[0] = vec4(color.rgb, 0.5); - gl_FragData[1] = vec4(0.5,0.5,0.5, 0.95); - gl_FragData[2] = vec4(wavef*0.5+0.5, 0.0); + gl_FragData[0] = vec4(color.rgb, 0.5); // diffuse + gl_FragData[1] = vec4(0.5,0.5,0.5, 0.95); // speccolor*spec, spec + gl_FragData[2] = vec4(wavef*0.5+0.5, 0.0); // normal } -- cgit v1.3 From 64797175a14785ac740ae27e647a5b0241700577 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Sun, 7 Mar 2010 11:34:31 +0000 Subject: Add a touch of fakey ripple to shadows on the surface of water, as if the water has depth. Using 4th component of the normal map to encode a position displacement (along the normal) in the class2 shadow lookup. --- indra/newview/app_settings/shaders/class1/deferred/waterF.glsl | 4 ++-- indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index 55c893d73b..ceb7e0fb56 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -133,9 +133,9 @@ void main() //wavef.z *= 0.1f; //wavef = normalize(wavef); - wavef = (norm_mat*vec4(wavef, 1.0)).xyz; + vec3 screenspacewavef = (norm_mat*vec4(wavef, 1.0)).xyz; gl_FragData[0] = vec4(color.rgb, 0.5); // diffuse gl_FragData[1] = vec4(0.5,0.5,0.5, 0.95); // speccolor*spec, spec - gl_FragData[2] = vec4(wavef*0.5+0.5, 0.0); // normal + gl_FragData[2] = vec4(screenspacewavef*0.5+0.5, screenspacewavef.z*0.5); // normal, displace } diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index 74a948f584..50502dbdad 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -159,7 +159,9 @@ void main() vec4 pos = getPosition(pos_screen); - vec3 norm = texture2DRect(normalMap, pos_screen).xyz*2.0-1.0; + vec4 nmap4 = texture2DRect(normalMap, pos_screen); + float displace = nmap4.w; + vec3 norm = nmap4.xyz*2.0-1.0; /*if (pos.z == 0.0) // do nothing for sky *FIX: REMOVE THIS IF/WHEN THE POSITION MAP IS BEING USED AS A STENCIL { @@ -168,9 +170,9 @@ void main() }*/ float shadow = 1.0; - float dp_directional_light = max(0.0, dot(norm, vary_light.xyz)); + float dp_directional_light = max(0.0, dot(norm, vary_light.xyz)); - vec4 spos = vec4(pos.xyz + vary_light.xyz * (1.0-dp_directional_light)*shadow_offset, 1.0); + vec4 spos = vec4(pos.xyz + displace*norm + vary_light.xyz * (1.0-dp_directional_light)*shadow_offset, 1.0); if (spos.z > -shadow_clip.w) { -- cgit v1.3 From 7c3bc40df78398b446a74b313b9068fc59db4756 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 17 Mar 2010 21:17:01 -0500 Subject: Fix for impostors being fullbright when deferred rendering enabled. --- indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl b/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl index 8c140a7b4f..508bbf415e 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/impostorF.glsl @@ -11,7 +11,8 @@ uniform sampler2D specularMap; void main() { - gl_FragData[0] = texture2D(diffuseMap, gl_TexCoord[0].xy); + vec4 col = texture2D(diffuseMap, gl_TexCoord[0].xy); + gl_FragData[0] = vec4(col.rgb, col.a <= 0.5 ? 0.0 : 0.005); gl_FragData[1] = texture2D(specularMap, gl_TexCoord[0].xy); gl_FragData[2] = vec4(texture2D(normalMap, gl_TexCoord[0].xy).xyz, 0.0); } -- cgit v1.3 From 0afe2491ab6a05bf62a2fce438dfdbbaf0b191df Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Wed, 17 Mar 2010 22:22:02 -0500 Subject: CTS-110 Remove soft alpha in deferred pipeline (breaks some content). Will revisit soft alpha in materials project. --- .../newview/app_settings/shaders/class1/deferred/alphaF.glsl | 11 ----------- .../app_settings/shaders/class1/deferred/fullbrightF.glsl | 11 ----------- .../newview/app_settings/shaders/class2/deferred/alphaF.glsl | 11 ----------- indra/newview/pipeline.cpp | 1 - 4 files changed, 34 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl index 4fb109d687..fea2e16090 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl @@ -24,8 +24,6 @@ varying vec3 vary_fragcoord; varying vec3 vary_position; varying vec3 vary_light; -uniform float alpha_soften; - uniform mat4 inv_proj; vec4 getPosition(vec2 pos_screen) @@ -57,15 +55,6 @@ void main() color.rgb = scaleSoftClip(color.rgb); - if (samp_pos.z != 0.0 && gl_Color.a < 1.0) - { - float dist_factor = alpha_soften; - float a = gl_Color.a; - a *= a; - dist_factor *= 1.0/(1.0-a); - color.a *= min((pos.z-samp_pos.z)*dist_factor, 1.0); - } - //gl_FragColor = gl_Color; gl_FragColor = color; //gl_FragColor = vec4(1,0,1,1); diff --git a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl index e518bddb98..0db9586a88 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/fullbrightF.glsl @@ -23,8 +23,6 @@ varying vec4 vary_position; varying vec3 vary_normal; varying vec3 vary_fragcoord; -uniform float alpha_soften; - uniform mat4 inv_proj; vec4 getPosition(vec2 pos_screen) @@ -56,15 +54,6 @@ void main() color.rgb = fullbrightScaleSoftClip(color.rgb); - if (samp_pos.z != 0.0 && color.a < 1.0) - { - float dist_factor = alpha_soften; - float a = color.a; - a *= a; - dist_factor *= 1.0/(1.0-a); - color.a *= min((pos.z-samp_pos.z)*dist_factor, 1.0); - } - //gl_FragColor = gl_Color; gl_FragColor = color; //gl_FragColor = vec4(1,0,1,1); diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index ad16de6d81..665fe16b43 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -29,8 +29,6 @@ varying vec3 vary_fragcoord; varying vec3 vary_position; varying vec3 vary_light; -uniform float alpha_soften; - uniform float shadow_bias; uniform mat4 inv_proj; @@ -115,15 +113,6 @@ void main() color.rgb = scaleSoftClip(color.rgb); - if (samp_pos.z != 0.0 && gl_Color.a < 1.0) - { - float dist_factor = alpha_soften; - float a = gl_Color.a; - a *= a; - dist_factor *= 1.0/(1.0-a); - color.a *= min((pos.z-samp_pos.z)*dist_factor, 1.0); - } - //gl_FragColor = gl_Color; gl_FragColor = color; //gl_FragColor = vec4(1,0,1,1)*shadow; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index e65c8b9af8..ee5ed32a83 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -6249,7 +6249,6 @@ void LLPipeline::bindDeferredShader(LLGLSLShader& shader, U32 light_index, LLRen shader.uniform2f("screen_res", mDeferredScreen.getWidth(), mDeferredScreen.getHeight()); shader.uniform1f("near_clip", LLViewerCamera::getInstance()->getNear()*2.f); - shader.uniform1f("alpha_soften", gSavedSettings.getF32("RenderDeferredAlphaSoften")); shader.uniform1f ("shadow_offset", gSavedSettings.getF32("RenderShadowOffset")); shader.uniform1f("shadow_bias", gSavedSettings.getF32("RenderShadowBias")); shader.uniform1f("lum_scale", gSavedSettings.getF32("RenderLuminanceScale")); -- cgit v1.3 From bbfacbd50d6e8beac36fd8de1dc51ceb109a4a7d Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Fri, 19 Mar 2010 18:14:17 -0500 Subject: RenderDeferredSSAO works as an independent setting -- decoupled shadows from SSAO. --- .../shaders/class1/deferred/blurLightF.glsl | 20 +- .../shaders/class1/deferred/multiSpotLightF.glsl | 3 +- .../shaders/class1/deferred/softenLightF.glsl | 27 ++- .../shaders/class1/deferred/spotLightF.glsl | 11 -- .../shaders/class1/deferred/sunLightF.glsl | 201 +-------------------- .../shaders/class2/deferred/multiSpotLightF.glsl | 3 - .../shaders/class2/deferred/softenLightF.glsl | 10 +- .../shaders/class2/deferred/spotLightF.glsl | 4 - .../shaders/class2/deferred/sunLightF.glsl | 64 +------ indra/newview/llviewercontrol.cpp | 1 + indra/newview/llviewershadermgr.cpp | 14 +- indra/newview/pipeline.cpp | 156 ++++++++-------- 12 files changed, 138 insertions(+), 376 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl index bd5e9dd758..0fad5b4b50 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl @@ -10,13 +10,11 @@ uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; uniform sampler2DRect lightMap; -uniform sampler2DRect giLightMap; uniform float dist_factor; uniform float blur_size; uniform vec2 delta; -uniform vec3 kern[32]; -uniform int kern_length; +uniform vec3 kern[4]; uniform float kern_scale; varying vec2 vary_fragcoord; @@ -50,7 +48,7 @@ void main() vec2 defined_weight = kern[0].xy; // special case the first (centre) sample's weight in the blur; we have to sample it anyway so we get it for 'free' vec4 col = defined_weight.xyxx * ccol; - for (int i = 1; i < kern_length; i++) + for (int i = 1; i < 4; i++) { vec2 tc = vary_fragcoord.xy + kern[i].z*dlt; vec3 samppos = getPosition(tc).xyz; @@ -61,12 +59,22 @@ void main() defined_weight += kern[i].xy; } } + for (int i = 1; i < 4; i++) + { + vec2 tc = vary_fragcoord.xy - kern[i].z*dlt; + vec3 samppos = getPosition(tc).xyz; + float d = dot(norm.xyz, samppos.xyz-pos.xyz);// dist from plane + if (d*d <= 0.003) + { + col += texture2DRect(lightMap, tc)*kern[i].xyxx; + defined_weight += kern[i].xy; + } + } col /= defined_weight.xyxx; gl_FragColor = col; - - //gl_FragColor = ccol; } + diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl index 2c8d90d1a6..82e9450e68 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiSpotLightF.glsl @@ -7,6 +7,8 @@ #version 120 +//class 1 -- no shadows + #extension GL_ARB_texture_rectangle : enable uniform sampler2DRect diffuseRect; @@ -14,7 +16,6 @@ uniform sampler2DRect specularRect; uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; uniform samplerCube environmentMap; -uniform sampler2DRect lightMap; uniform sampler2D noiseMap; uniform sampler2D lightFunc; uniform sampler2D projectionMap; diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index b4b0d0ce9d..9cf60aad48 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -11,6 +11,7 @@ uniform sampler2DRect diffuseRect; uniform sampler2DRect specularRect; uniform sampler2DRect positionMap; uniform sampler2DRect normalMap; +uniform sampler2DRect lightMap; uniform sampler2DRect depthMap; uniform sampler2D noiseMap; uniform samplerCube environmentMap; @@ -40,7 +41,7 @@ uniform float scene_light_strength; uniform vec3 env_mat[3]; //uniform mat4 shadow_matrix[3]; //uniform vec4 shadow_clip; -//uniform mat3 ssao_effect_mat; +uniform mat3 ssao_effect_mat; varying vec4 vary_light; varying vec2 vary_fragcoord; @@ -178,7 +179,17 @@ void calcAtmospherics(vec3 inPositionEye, float ambFactor) { temp2.x += .25; //increase ambient when there are more clouds - vec4 tmpAmbient = ambient + (vec4(1.) - ambient) * cloud_shadow.x * 0.5; + vec4 tmpAmbient = ambient + (vec4(1.) - ambient) * cloud_shadow.x * 0.5; + + /* decrease value and saturation (that in HSV, not HSL) for occluded areas + * // for HSV color/geometry used here, see http://gimp-savvy.com/BOOK/index.html?node52.html + * // The following line of code performs the equivalent of: + * float ambAlpha = tmpAmbient.a; + * float ambValue = dot(vec3(tmpAmbient), vec3(0.577)); // projection onto <1/rt(3), 1/rt(3), 1/rt(3)>, the neutral white-black axis + * vec3 ambHueSat = vec3(tmpAmbient) - vec3(ambValue); + * tmpAmbient = vec4(RenderSSAOEffect.valueFactor * vec3(ambValue) + RenderSSAOEffect.saturationFactor *(1.0 - ambFactor) * ambHueSat, ambAlpha); + */ + tmpAmbient = vec4(mix(ssao_effect_mat * tmpAmbient.rgb, tmpAmbient.rgb, ambFactor), tmpAmbient.a); //haze color setAdditiveColor( @@ -250,10 +261,14 @@ void main() vec4 diffuse = texture2DRect(diffuseRect, tc); vec4 spec = texture2DRect(specularRect, vary_fragcoord.xy); - calcAtmospherics(pos.xyz, 0.0); + vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; + float scol = max(scol_ambocc.r, diffuse.a); + float ambocc = scol_ambocc.g; + + calcAtmospherics(pos.xyz, ambocc); vec3 col = atmosAmbient(vec3(0)); - col += atmosAffectDirectionalLight(clamp(da, diffuse.a, 1.0)); + col += atmosAffectDirectionalLight(max(min(da, scol), diffuse.a)); col *= diffuse.rgb; @@ -261,12 +276,12 @@ void main() { vec3 ref = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(ref, vary_light.xyz); - col.rgb += vary_SunlitColor*spec.rgb*texture2D(lightFunc, vec2(sa, spec.a)).a; + col.rgb += vary_SunlitColor*scol_ambocc.r*spec.rgb*texture2D(lightFunc, vec2(sa, spec.a)).a; } col = atmosLighting(col); col = scaleSoftClip(col); - + gl_FragColor.rgb = col; gl_FragColor.a = 0.0; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl index 2a7234fd83..f320dbb400 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl @@ -161,17 +161,6 @@ void main() } } - /*if (spec.a > 0.0) - { - //vec3 ref = reflect(normalize(pos), norm); - float sa = dot(normalize(lv-normalize(pos)),norm);; - //sa = max(sa, 0.0); - //sa = pow(sa, 128.0 * spec.a*spec.a/dist_atten)*min(dist_atten*4.0, 1.0); - sa = texture2D(lightFunc, vec2(sa, spec.a)).a * min(dist_atten*4.0, 1.0); - sa *= noise; - col += da*sa*lcol*spec.rgb; - }*/ - gl_FragColor.rgb = col; gl_FragColor.a = 0.0; } diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl index fafc2ae3cc..56e4055c02 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightF.glsl @@ -5,206 +5,11 @@ * $License$ */ -#extension GL_ARB_texture_rectangle : enable - -uniform sampler2DRect depthMap; -uniform sampler2DRect normalMap; -uniform sampler2DRectShadow shadowMap0; -uniform sampler2DRectShadow shadowMap1; -uniform sampler2DRectShadow shadowMap2; -uniform sampler2DRectShadow shadowMap3; -uniform sampler2DRectShadow shadowMap4; -uniform sampler2DRectShadow shadowMap5; -uniform sampler2D noiseMap; - -uniform sampler2D lightFunc; - - -// Inputs -uniform mat4 shadow_matrix[6]; -uniform vec4 shadow_clip; -uniform float ssao_radius; -uniform float ssao_max_radius; -uniform float ssao_factor; -uniform float ssao_factor_inv; - -varying vec2 vary_fragcoord; -varying vec4 vary_light; - -uniform mat4 inv_proj; -uniform vec2 screen_res; - -uniform float shadow_bias; -uniform float shadow_offset; - -vec4 getPosition(vec2 pos_screen) -{ - float depth = texture2DRect(depthMap, pos_screen.xy).a; - vec2 sc = pos_screen.xy*2.0; - sc /= screen_res; - sc -= vec2(1.0,1.0); - vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0); - vec4 pos = inv_proj * ndc; - pos /= pos.w; - pos.w = 1.0; - return pos; -} +//class 1, no shadow, no SSAO, should never be called -//calculate decreases in ambient lighting when crowded out (SSAO) -float calcAmbientOcclusion(vec4 pos, vec3 norm) -{ - float ret = 1.0; - - float dist = dot(pos.xyz,pos.xyz); - - if (dist < 64.0*64.0) - { - vec2 kern[8]; - // exponentially (^2) distant occlusion samples spread around origin - kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; - kern[1] = vec2(1.0, 0.0) * 0.250*0.250; - kern[2] = vec2(0.0, 1.0) * 0.375*0.375; - kern[3] = vec2(0.0, -1.0) * 0.500*0.500; - kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; - kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; - kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; - kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; - - vec2 pos_screen = vary_fragcoord.xy; - vec3 pos_world = pos.xyz; - vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; - - float angle_hidden = 0.0; - int points = 0; - - float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); - - // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?) - for (int i = 0; i < 8; i++) - { - vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect); - vec3 samppos_world = getPosition(samppos_screen).xyz; - - vec3 diff = pos_world - samppos_world; - float dist2 = dot(diff, diff); - - // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area - // --> solid angle shrinking by the square of distance - //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 - //(k should vary inversely with # of samples, but this is taken care of later) - - //if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) // -0.05*norm to shift sample point back slightly for flat surfaces - // angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional. max of 1.0 (= ssao_factor_inv * ssao_factor) - angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); - - // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" - points = points + int(diff.z > -1.0); - } - - angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); - - ret = (1.0 - (float(points != 0) * angle_hidden)); - ret += max((dist-32.0*32.0)/(32.0*32.0), 0.0); - } - - return min(ret, 1.0); -} +#extension GL_ARB_texture_rectangle : enable void main() { - vec2 pos_screen = vary_fragcoord.xy; - - //try doing an unproject here - - vec4 pos = getPosition(pos_screen); - - vec3 norm = texture2DRect(normalMap, pos_screen).xyz*2.0-1.0; - - /*if (pos.z == 0.0) // do nothing for sky *FIX: REMOVE THIS IF/WHEN THE POSITION MAP IS BEING USED AS A STENCIL - { - gl_FragColor = vec4(0.0); // doesn't matter - return; - }*/ - - float shadow = 1.0; - float dp_directional_light = max(0.0, dot(norm, vary_light.xyz)); - - vec4 spos = vec4(pos.xyz + norm.xyz * (-pos.z/64.0*shadow_offset+shadow_bias), 1.0); - - //vec3 debug = vec3(0,0,0); - - if (dp_directional_light == 0.0) - { - // if we know this point is facing away from the sun then we know it's in shadow without having to do a squirrelly shadow-map lookup - shadow = 0.0; - } - else if (spos.z > -shadow_clip.w) - { - vec4 lpos; - - if (spos.z < -shadow_clip.z) - { - lpos = shadow_matrix[3]*spos; - lpos.xy *= screen_res; - shadow = shadow2DRectProj(shadowMap3, lpos).x; - shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0); - } - else if (spos.z < -shadow_clip.y) - { - lpos = shadow_matrix[2]*spos; - lpos.xy *= screen_res; - shadow = shadow2DRectProj(shadowMap2, lpos).x; - } - else if (spos.z < -shadow_clip.x) - { - lpos = shadow_matrix[1]*spos; - lpos.xy *= screen_res; - shadow = shadow2DRectProj(shadowMap1, lpos).x; - } - else - { - lpos = shadow_matrix[0]*spos; - lpos.xy *= screen_res; - shadow = shadow2DRectProj(shadowMap0, lpos).x; - } - - // take the most-shadowed value out of these two: - // * the blurred sun shadow in the light (shadow) map - // * an unblurred dot product between the sun and this norm - // the goal is to err on the side of most-shadow to fill-in shadow holes and reduce artifacting - shadow = min(shadow, dp_directional_light); - - /*debug.r = lpos.y / (lpos.w*screen_res.y); - - lpos.xy /= lpos.w*32.0; - if (fract(lpos.x) < 0.1 || fract(lpos.y) < 0.1) - { - debug.gb = vec2(0.5, 0.5); - } - - debug += (1.0-shadow)*0.5;*/ - - } - else - { - // more distant than the shadow map covers - just use directional shading as shadow - shadow = dp_directional_light; - } - - gl_FragColor[0] = shadow; - gl_FragColor[1] = calcAmbientOcclusion(pos, norm); - - //spotlight shadow 1 - vec4 lpos = shadow_matrix[4]*spos; - lpos.xy *= screen_res; - gl_FragColor[2] = shadow2DRectProj(shadowMap4, lpos).x; - - //spotlight shadow 2 - lpos = shadow_matrix[5]*spos; - lpos.xy *= screen_res; - gl_FragColor[3] = shadow2DRectProj(shadowMap5, lpos).x; - - //gl_FragColor.rgb = pos.xyz; - //gl_FragColor.b = shadow; - //gl_FragColor.rgb = debug; + gl_FragColor = vec4(0,0,0,0); } diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl index 3e29c684e8..5308e5bb1e 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl @@ -230,9 +230,6 @@ void main() } } - //attenuate point light contribution by SSAO component - col *= texture2DRect(lightMap, frag.xy).g; - gl_FragColor.rgb = col; gl_FragColor.a = 0.0; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 249ac7ef9b..589a292751 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -11,6 +11,7 @@ uniform sampler2DRect diffuseRect; uniform sampler2DRect specularRect; uniform sampler2DRect normalMap; uniform sampler2DRect lightMap; +uniform sampler2DRect depthMap; uniform sampler2D noiseMap; uniform samplerCube environmentMap; uniform sampler2D lightFunc; @@ -41,7 +42,6 @@ uniform vec3 env_mat[3]; uniform vec4 shadow_clip; uniform mat3 ssao_effect_mat; -uniform sampler2DRect depthMap; uniform mat4 inv_proj; uniform vec2 screen_res; @@ -282,13 +282,5 @@ void main() col = scaleSoftClip(col); gl_FragColor.rgb = col; - - //gl_FragColor.rgb = gi_col.rgb; gl_FragColor.a = 0.0; - - //gl_FragColor.rg = scol_ambocc.rg; - //gl_FragColor.rgb = texture2DRect(lightMap, vary_fragcoord.xy).rgb; - //gl_FragColor.rgb = norm.rgb*0.5+0.5; - //gl_FragColor.rgb = vec3(ambocc); - //gl_FragColor.rgb = vec3(scol); } diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl index 6617e33c0d..eeaecc157f 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl @@ -179,10 +179,6 @@ void main() } } - //attenuate point light contribution by SSAO component - col *= texture2DRect(lightMap, frag.xy).g; - - gl_FragColor.rgb = col; gl_FragColor.a = 0.0; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index 50502dbdad..04c9a4d19a 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -7,6 +7,8 @@ #extension GL_ARB_texture_rectangle : enable +//class 2, shadows, no SSAO + uniform sampler2DRect depthMap; uniform sampler2DRect normalMap; uniform sampler2DRectShadow shadowMap0; @@ -52,66 +54,6 @@ vec4 getPosition(vec2 pos_screen) return pos; } -//calculate decreases in ambient lighting when crowded out (SSAO) -float calcAmbientOcclusion(vec4 pos, vec3 norm) -{ - float ret = 1.0; - - float dist = dot(pos.xyz,pos.xyz); - - if (dist < 64.0*64.0) - { - vec2 kern[8]; - // exponentially (^2) distant occlusion samples spread around origin - kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; - kern[1] = vec2(1.0, 0.0) * 0.250*0.250; - kern[2] = vec2(0.0, 1.0) * 0.375*0.375; - kern[3] = vec2(0.0, -1.0) * 0.500*0.500; - kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; - kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; - kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; - kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; - - vec2 pos_screen = vary_fragcoord.xy; - vec3 pos_world = pos.xyz; - vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; - - float angle_hidden = 0.0; - int points = 0; - - float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); - - // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?) - for (int i = 0; i < 8; i++) - { - vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect); - vec3 samppos_world = getPosition(samppos_screen).xyz; - - vec3 diff = pos_world - samppos_world; - float dist2 = dot(diff, diff); - - // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area - // --> solid angle shrinking by the square of distance - //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 - //(k should vary inversely with # of samples, but this is taken care of later) - - //if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) // -0.05*norm to shift sample point back slightly for flat surfaces - // angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional. max of 1.0 (= ssao_factor_inv * ssao_factor) - angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); - - // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" - points = points + int(diff.z > -1.0); - } - - angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); - - ret = (1.0 - (float(points != 0) * angle_hidden)); - ret += max((dist-32.0*32.0)/(32.0*32.0), 0.0); - } - - return min(ret, 1.0); -} - float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) { stc.xyz /= stc.w; @@ -232,7 +174,7 @@ void main() } gl_FragColor[0] = shadow; - gl_FragColor[1] = calcAmbientOcclusion(pos, norm); + gl_FragColor[1] = 1.0; //spotlight shadow 1 vec4 lpos = shadow_matrix[4]*spos; diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 8051fcbcfa..862e1de685 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -547,6 +547,7 @@ void settings_setup_listeners() gSavedSettings.getControl("RenderResolutionDivisor")->getSignal()->connect(boost::bind(&handleRenderResolutionDivisorChanged, _2)); gSavedSettings.getControl("RenderDeferred")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("RenderShadowDetail")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); + gSavedSettings.getControl("RenderDeferredSSAO")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("RenderDeferredGI")->getSignal()->connect(boost::bind(&handleSetShaderChanged, _2)); gSavedSettings.getControl("TextureMemory")->getSignal()->connect(boost::bind(&handleVideoMemoryChanged, _2)); gSavedSettings.getControl("AuditTexture")->getSignal()->connect(boost::bind(&handleAuditTextureChanged, _2)); diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 5b7b464e91..50e5522174 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -973,10 +973,22 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() if (success) { + S32 level = 0; + std::string fragment; + + if (gSavedSettings.getBOOL("RenderDeferredSSAO")) + { + fragment = "deferred/sunLightSSAOF.glsl"; + } + else + { + fragment = "deferred/sunLightF.glsl"; + } + gDeferredSunProgram.mName = "Deferred Sun Shader"; gDeferredSunProgram.mShaderFiles.clear(); gDeferredSunProgram.mShaderFiles.push_back(make_pair("deferred/sunLightV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredSunProgram.mShaderFiles.push_back(make_pair("deferred/sunLightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSunProgram.mShaderFiles.push_back(make_pair(fragment, GL_FRAGMENT_SHADER_ARB)); gDeferredSunProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredSunProgram.createShader(NULL, NULL); } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 04118ad767..5cfd6c3d38 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -6344,16 +6344,16 @@ void LLPipeline::renderDeferredLighting() glTexCoord4f(tc.v[0], tc.v[1], tc.v[2], 0); } - if (gSavedSettings.getS32("RenderShadowDetail") > 0) - { - glPushMatrix(); - glLoadIdentity(); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); - mDeferredLight[0].bindTarget(); - if (gSavedSettings.getBOOL("RenderDeferredSun")) + mDeferredLight[0].bindTarget(); + + if (gSavedSettings.getBOOL("RenderDeferredSSAO") || gSavedSettings.getS32("RenderShadowDetail") > 0) + { { //paint shadow/SSAO light map (direct lighting lightmap) LLFastTimer ftm(FTM_SUN_SHADOW); bindDeferredShader(gDeferredSunProgram, 0); @@ -6394,18 +6394,22 @@ void LLPipeline::renderDeferredLighting() unbindDeferredShader(gDeferredSunProgram); } - else - { - mDeferredLight[0].clear(GL_COLOR_BUFFER_BIT); - } + } + else + { + glClearColor(1,1,1,1); + mDeferredLight[0].clear(GL_COLOR_BUFFER_BIT); + glClearColor(0,0,0,0); + } - mDeferredLight[0].flush(); + mDeferredLight[0].flush(); + { //global illumination specific block (still experimental) if (gSavedSettings.getBOOL("RenderDeferredBlurLight") && - gSavedSettings.getBOOL("RenderDeferredGI")) - { + gSavedSettings.getBOOL("RenderDeferredGI")) + { LLFastTimer ftm(FTM_EDGE_DETECTION); - //get edge map + //generate edge map LLGLDisable blend(GL_BLEND); LLGLDisable test(GL_ALPHA_TEST); LLGLDepthTest depth(GL_FALSE); @@ -6502,79 +6506,79 @@ void LLPipeline::renderDeferredLighting() unbindDeferredShader(gDeferredPostGIProgram); } } + } - if (gSavedSettings.getBOOL("RenderDeferredBlurLight")) - { //soften direct lighting lightmap - LLFastTimer ftm(FTM_SOFTEN_SHADOW); - //blur lightmap - mDeferredLight[1].bindTarget(); + if (gSavedSettings.getBOOL("RenderDeferredSSAO")) + { //soften direct lighting lightmap + LLFastTimer ftm(FTM_SOFTEN_SHADOW); + //blur lightmap + mDeferredLight[1].bindTarget(); - glClearColor(1,1,1,1); - mDeferredLight[1].clear(GL_COLOR_BUFFER_BIT); - glClearColor(0,0,0,0); - - bindDeferredShader(gDeferredBlurLightProgram); + glClearColor(1,1,1,1); + mDeferredLight[1].clear(GL_COLOR_BUFFER_BIT); + glClearColor(0,0,0,0); + + bindDeferredShader(gDeferredBlurLightProgram); - LLVector3 go = gSavedSettings.getVector3("RenderShadowGaussian"); - const U32 kern_length = 4; - F32 blur_size = gSavedSettings.getF32("RenderShadowBlurSize"); - F32 dist_factor = gSavedSettings.getF32("RenderShadowBlurDistFactor"); + LLVector3 go = gSavedSettings.getVector3("RenderShadowGaussian"); + const U32 kern_length = 4; + F32 blur_size = gSavedSettings.getF32("RenderShadowBlurSize"); + F32 dist_factor = gSavedSettings.getF32("RenderShadowBlurDistFactor"); - // sample symmetrically with the middle sample falling exactly on 0.0 - F32 x = 0.f; + // sample symmetrically with the middle sample falling exactly on 0.0 + F32 x = 0.f; - LLVector3 gauss[32]; // xweight, yweight, offset + LLVector3 gauss[32]; // xweight, yweight, offset - for (U32 i = 0; i < kern_length; i++) - { - gauss[i].mV[0] = llgaussian(x, go.mV[0]); - gauss[i].mV[1] = llgaussian(x, go.mV[1]); - gauss[i].mV[2] = x; - x += 1.f; - } + for (U32 i = 0; i < kern_length; i++) + { + gauss[i].mV[0] = llgaussian(x, go.mV[0]); + gauss[i].mV[1] = llgaussian(x, go.mV[1]); + gauss[i].mV[2] = x; + x += 1.f; + } - gDeferredBlurLightProgram.uniform2f("delta", 1.f, 0.f); - gDeferredBlurLightProgram.uniform1f("dist_factor", dist_factor); - gDeferredBlurLightProgram.uniform3fv("kern[0]", kern_length, gauss[0].mV); - gDeferredBlurLightProgram.uniform3fv("kern", kern_length, gauss[0].mV); - gDeferredBlurLightProgram.uniform1f("kern_scale", blur_size * (kern_length/2.f - 0.5f)); + gDeferredBlurLightProgram.uniform2f("delta", 1.f, 0.f); + gDeferredBlurLightProgram.uniform1f("dist_factor", dist_factor); + gDeferredBlurLightProgram.uniform3fv("kern[0]", kern_length, gauss[0].mV); + gDeferredBlurLightProgram.uniform3fv("kern", kern_length, gauss[0].mV); + gDeferredBlurLightProgram.uniform1f("kern_scale", blur_size * (kern_length/2.f - 0.5f)); + + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + stop_glerror(); + } - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - stop_glerror(); - } - - mDeferredLight[1].flush(); - unbindDeferredShader(gDeferredBlurLightProgram); + mDeferredLight[1].flush(); + unbindDeferredShader(gDeferredBlurLightProgram); - bindDeferredShader(gDeferredBlurLightProgram, 1); - mDeferredLight[0].bindTarget(); + bindDeferredShader(gDeferredBlurLightProgram, 1); + mDeferredLight[0].bindTarget(); - gDeferredBlurLightProgram.uniform2f("delta", 0.f, 1.f); + gDeferredBlurLightProgram.uniform2f("delta", 0.f, 1.f); - { - LLGLDisable blend(GL_BLEND); - LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); - stop_glerror(); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - stop_glerror(); - } - mDeferredLight[0].flush(); - unbindDeferredShader(gDeferredBlurLightProgram); + { + LLGLDisable blend(GL_BLEND); + LLGLDepthTest depth(GL_TRUE, GL_FALSE, GL_ALWAYS); + stop_glerror(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + stop_glerror(); } - - stop_glerror(); - glPopMatrix(); - stop_glerror(); - glMatrixMode(GL_MODELVIEW); - stop_glerror(); - glPopMatrix(); - stop_glerror(); + mDeferredLight[0].flush(); + unbindDeferredShader(gDeferredBlurLightProgram); } + stop_glerror(); + glPopMatrix(); + stop_glerror(); + glMatrixMode(GL_MODELVIEW); + stop_glerror(); + glPopMatrix(); + stop_glerror(); + //copy depth and stencil from deferred screen //mScreen.copyContents(mDeferredScreen, 0, 0, mDeferredScreen.getWidth(), mDeferredScreen.getHeight(), // 0, 0, mScreen.getWidth(), mScreen.getHeight(), GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); -- cgit v1.3 From 8f9fae7815f24ca59e2a7e180b7baab3bc75e2e7 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Sat, 20 Mar 2010 14:15:13 -0500 Subject: SSAO version of sunLight shaders. --- .../shaders/class1/deferred/sunLightSSAOF.glsl | 123 ++++++++++ .../shaders/class2/deferred/sunLightSSAOF.glsl | 248 +++++++++++++++++++++ 2 files changed, 371 insertions(+) create mode 100644 indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl create mode 100644 indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl new file mode 100644 index 0000000000..7450817ea7 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl @@ -0,0 +1,123 @@ +/** + * @file sunLightSSAOF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#extension GL_ARB_texture_rectangle : enable + +//class 1 -- no shadow, SSAO only + +uniform sampler2DRect depthMap; +uniform sampler2DRect normalMap; +uniform sampler2D noiseMap; + +uniform sampler2D lightFunc; + + +// Inputs +uniform mat4 shadow_matrix[6]; +uniform vec4 shadow_clip; +uniform float ssao_radius; +uniform float ssao_max_radius; +uniform float ssao_factor; +uniform float ssao_factor_inv; + +varying vec2 vary_fragcoord; +varying vec4 vary_light; + +uniform mat4 inv_proj; +uniform vec2 screen_res; + +uniform float shadow_bias; +uniform float shadow_offset; + +vec4 getPosition(vec2 pos_screen) +{ + float depth = texture2DRect(depthMap, pos_screen.xy).a; + vec2 sc = pos_screen.xy*2.0; + sc /= screen_res; + sc -= vec2(1.0,1.0); + vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0); + vec4 pos = inv_proj * ndc; + pos /= pos.w; + pos.w = 1.0; + return pos; +} + +//calculate decreases in ambient lighting when crowded out (SSAO) +float calcAmbientOcclusion(vec4 pos, vec3 norm) +{ + float ret = 1.0; + + float dist = dot(pos.xyz,pos.xyz); + + if (dist < 64.0*64.0) + { + vec2 kern[8]; + // exponentially (^2) distant occlusion samples spread around origin + kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; + kern[1] = vec2(1.0, 0.0) * 0.250*0.250; + kern[2] = vec2(0.0, 1.0) * 0.375*0.375; + kern[3] = vec2(0.0, -1.0) * 0.500*0.500; + kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; + kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; + kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; + kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; + + vec2 pos_screen = vary_fragcoord.xy; + vec3 pos_world = pos.xyz; + vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; + + float angle_hidden = 0.0; + int points = 0; + + float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); + + // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?) + for (int i = 0; i < 8; i++) + { + vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect); + vec3 samppos_world = getPosition(samppos_screen).xyz; + + vec3 diff = pos_world - samppos_world; + float dist2 = dot(diff, diff); + + // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area + // --> solid angle shrinking by the square of distance + //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 + //(k should vary inversely with # of samples, but this is taken care of later) + + //if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) // -0.05*norm to shift sample point back slightly for flat surfaces + // angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional. max of 1.0 (= ssao_factor_inv * ssao_factor) + angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); + + // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" + points = points + int(diff.z > -1.0); + } + + angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); + + ret = (1.0 - (float(points != 0) * angle_hidden)); + ret += max((dist-32.0*32.0)/(32.0*32.0), 0.0); + } + + return min(ret, 1.0); +} + +void main() +{ + vec2 pos_screen = vary_fragcoord.xy; + + //try doing an unproject here + + vec4 pos = getPosition(pos_screen); + + vec3 norm = texture2DRect(normalMap, pos_screen).xyz*2.0-1.0; + + gl_FragColor[0] = 1.0; + gl_FragColor[1] = calcAmbientOcclusion(pos, norm); + gl_FragColor[2] = 1.0; + gl_FragColor[3] = 1.0; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl new file mode 100644 index 0000000000..d77d17942a --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl @@ -0,0 +1,248 @@ +/** + * @file sunLightSSAOF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#extension GL_ARB_texture_rectangle : enable + +//class 2 -- shadows and SSAO + +uniform sampler2DRect depthMap; +uniform sampler2DRect normalMap; +uniform sampler2DRectShadow shadowMap0; +uniform sampler2DRectShadow shadowMap1; +uniform sampler2DRectShadow shadowMap2; +uniform sampler2DRectShadow shadowMap3; +uniform sampler2DShadow shadowMap4; +uniform sampler2DShadow shadowMap5; +uniform sampler2D noiseMap; + +uniform sampler2D lightFunc; + +// Inputs +uniform mat4 shadow_matrix[6]; +uniform vec4 shadow_clip; +uniform float ssao_radius; +uniform float ssao_max_radius; +uniform float ssao_factor; +uniform float ssao_factor_inv; + +varying vec2 vary_fragcoord; +varying vec4 vary_light; + +uniform mat4 inv_proj; +uniform vec2 screen_res; +uniform vec2 shadow_res; +uniform vec2 proj_shadow_res; + +uniform float shadow_bias; +uniform float shadow_offset; + +vec4 getPosition(vec2 pos_screen) +{ + float depth = texture2DRect(depthMap, pos_screen.xy).a; + vec2 sc = pos_screen.xy*2.0; + sc /= screen_res; + sc -= vec2(1.0,1.0); + vec4 ndc = vec4(sc.x, sc.y, 2.0*depth-1.0, 1.0); + vec4 pos = inv_proj * ndc; + pos /= pos.w; + pos.w = 1.0; + return pos; +} + +//calculate decreases in ambient lighting when crowded out (SSAO) +float calcAmbientOcclusion(vec4 pos, vec3 norm) +{ + float ret = 1.0; + + float dist = dot(pos.xyz,pos.xyz); + + if (dist < 64.0*64.0) + { + vec2 kern[8]; + // exponentially (^2) distant occlusion samples spread around origin + kern[0] = vec2(-1.0, 0.0) * 0.125*0.125; + kern[1] = vec2(1.0, 0.0) * 0.250*0.250; + kern[2] = vec2(0.0, 1.0) * 0.375*0.375; + kern[3] = vec2(0.0, -1.0) * 0.500*0.500; + kern[4] = vec2(0.7071, 0.7071) * 0.625*0.625; + kern[5] = vec2(-0.7071, -0.7071) * 0.750*0.750; + kern[6] = vec2(-0.7071, 0.7071) * 0.875*0.875; + kern[7] = vec2(0.7071, -0.7071) * 1.000*1.000; + + vec2 pos_screen = vary_fragcoord.xy; + vec3 pos_world = pos.xyz; + vec2 noise_reflect = texture2D(noiseMap, vary_fragcoord.xy/128.0).xy; + + float angle_hidden = 0.0; + int points = 0; + + float scale = min(ssao_radius / -pos_world.z, ssao_max_radius); + + // it was found that keeping # of samples a constant was the fastest, probably due to compiler optimizations (unrolling?) + for (int i = 0; i < 8; i++) + { + vec2 samppos_screen = pos_screen + scale * reflect(kern[i], noise_reflect); + vec3 samppos_world = getPosition(samppos_screen).xyz; + + vec3 diff = pos_world - samppos_world; + float dist2 = dot(diff, diff); + + // assume each sample corresponds to an occluding sphere with constant radius, constant x-sectional area + // --> solid angle shrinking by the square of distance + //radius is somewhat arbitrary, can approx with just some constant k * 1 / dist^2 + //(k should vary inversely with # of samples, but this is taken care of later) + + //if (dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) // -0.05*norm to shift sample point back slightly for flat surfaces + // angle_hidden += min(1.0/dist2, ssao_factor_inv); // dist != 0 follows from conditional. max of 1.0 (= ssao_factor_inv * ssao_factor) + angle_hidden = angle_hidden + float(dot((samppos_world - 0.05*norm - pos_world), norm) > 0.0) * min(1.0/dist2, ssao_factor_inv); + + // 'blocked' samples (significantly closer to camera relative to pos_world) are "no data", not "no occlusion" + points = points + int(diff.z > -1.0); + } + + angle_hidden = min(ssao_factor*angle_hidden/float(points), 1.0); + + ret = (1.0 - (float(points != 0) * angle_hidden)); + ret += max((dist-32.0*32.0)/(32.0*32.0), 0.0); + } + + return min(ret, 1.0); +} + +float pcfShadow(sampler2DRectShadow shadowMap, vec4 stc, float scl) +{ + stc.xyz /= stc.w; + stc.z += shadow_bias*scl; + + float cs = shadow2DRect(shadowMap, stc.xyz).x; + float shadow = cs; + + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, 1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(1.5, -1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, 1.5, 0.0)).x, cs); + shadow += max(shadow2DRect(shadowMap, stc.xyz+vec3(-1.5, -1.5, 0.0)).x, cs); + + return shadow/5.0; + + //return shadow; +} + +float pcfShadow(sampler2DShadow shadowMap, vec4 stc, float scl) +{ + stc.xyz /= stc.w; + stc.z += shadow_bias*scl; + + float cs = shadow2D(shadowMap, stc.xyz).x; + float shadow = cs; + + vec2 off = 1.5/proj_shadow_res; + + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(off.x, -off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, off.y, 0.0)).x, cs); + shadow += max(shadow2D(shadowMap, stc.xyz+vec3(-off.x, -off.y, 0.0)).x, cs); + + + return shadow/5.0; + + //return shadow; +} + +void main() +{ + vec2 pos_screen = vary_fragcoord.xy; + + //try doing an unproject here + + vec4 pos = getPosition(pos_screen); + + vec4 nmap4 = texture2DRect(normalMap, pos_screen); + float displace = nmap4.w; + vec3 norm = nmap4.xyz*2.0-1.0; + + /*if (pos.z == 0.0) // do nothing for sky *FIX: REMOVE THIS IF/WHEN THE POSITION MAP IS BEING USED AS A STENCIL + { + gl_FragColor = vec4(0.0); // doesn't matter + return; + }*/ + + float shadow = 1.0; + float dp_directional_light = max(0.0, dot(norm, vary_light.xyz)); + + vec4 spos = vec4(pos.xyz + displace*norm + vary_light.xyz * (1.0-dp_directional_light)*shadow_offset, 1.0); + + if (spos.z > -shadow_clip.w) + { + if (dp_directional_light == 0.0) + { + // if we know this point is facing away from the sun then we know it's in shadow without having to do a squirrelly shadow-map lookup + shadow = 0.0; + } + else + { + vec4 lpos; + + if (spos.z < -shadow_clip.z) + { + lpos = shadow_matrix[3]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap3, lpos, 0.25); + shadow += max((pos.z+shadow_clip.z)/(shadow_clip.z-shadow_clip.w)*2.0-1.0, 0.0); + } + else if (spos.z < -shadow_clip.y) + { + lpos = shadow_matrix[2]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap2, lpos, 0.5); + } + else if (spos.z < -shadow_clip.x) + { + lpos = shadow_matrix[1]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap1, lpos, 0.75); + } + else + { + lpos = shadow_matrix[0]*spos; + lpos.xy *= shadow_res; + shadow = pcfShadow(shadowMap0, lpos, 1.0); + } + + // take the most-shadowed value out of these two: + // * the blurred sun shadow in the light (shadow) map + // * an unblurred dot product between the sun and this norm + // the goal is to err on the side of most-shadow to fill-in shadow holes and reduce artifacting + shadow = min(shadow, dp_directional_light); + + //lpos.xy /= lpos.w*32.0; + //if (fract(lpos.x) < 0.1 || fract(lpos.y) < 0.1) + //{ + // shadow = 0.0; + //} + + } + } + else + { + // more distant than the shadow map covers + shadow = 1.0; + } + + gl_FragColor[0] = shadow; + gl_FragColor[1] = calcAmbientOcclusion(pos, norm); + + //spotlight shadow 1 + vec4 lpos = shadow_matrix[4]*spos; + gl_FragColor[2] = pcfShadow(shadowMap4, lpos, 0.8).x; + + //spotlight shadow 2 + lpos = shadow_matrix[5]*spos; + gl_FragColor[3] = pcfShadow(shadowMap5, lpos, 0.8).x; + + //gl_FragColor.rgb = pos.xyz; + //gl_FragColor.b = shadow; +} -- cgit v1.3 From eb3206fb93187efdef3dce389862ed31be516963 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Mon, 22 Mar 2010 16:10:34 +0000 Subject: screen-space reflections mk4. generally subtle. very cheap. --- .../shaders/class1/deferred/softenLightF.glsl | 47 +++++++++++++++++---- .../shaders/class2/deferred/softenLightF.glsl | 48 ++++++++++++++++++---- .../shaders/class3/deferred/softenLightF.glsl | 48 ++++++++++++++++++---- 3 files changed, 119 insertions(+), 24 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 9cf60aad48..7d57eaa13d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -56,9 +56,8 @@ vec3 vary_AtmosAttenuation; uniform mat4 inv_proj; uniform vec2 screen_res; -vec4 getPosition(vec2 pos_screen) -{ //get position in screen space (world units) given window coordinate and depth map - float depth = texture2DRect(depthMap, pos_screen.xy).a; +vec4 getPosition_d(vec2 pos_screen, float depth) +{ vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); @@ -69,6 +68,12 @@ vec4 getPosition(vec2 pos_screen) return pos; } +vec4 getPosition(vec2 pos_screen) +{ //get position in screen space (world units) given window coordinate and depth map + float depth = texture2DRect(depthMap, pos_screen.xy).a; + return getPosition_d(pos_screen, depth); +} + vec3 getPositionEye() { return vary_PositionEye; @@ -252,7 +257,8 @@ vec3 scaleSoftClip(vec3 light) void main() { vec2 tc = vary_fragcoord.xy; - vec3 pos = getPosition(tc).xyz; + float depth = texture2DRect(depthMap, tc.xy).a; + vec3 pos = getPosition_d(tc, depth).xyz; vec3 norm = texture2DRect(normalMap, tc).xyz*2.0-1.0; //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz; @@ -272,11 +278,36 @@ void main() col *= diffuse.rgb; - if (spec.a > 0.0) + if (spec.a > 0.0) // specular reflection { - vec3 ref = normalize(reflect(pos.xyz, norm.xyz)); - float sa = dot(ref, vary_light.xyz); - col.rgb += vary_SunlitColor*scol_ambocc.r*spec.rgb*texture2D(lightFunc, vec2(sa, spec.a)).a; + // the old infinite-sky shiny reflection + // + vec3 refnorm = normalize(reflect(pos.xyz, norm.xyz)); + float sa = dot(refnorm, vary_light.xyz); + vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; + + // screen-space cheap fakey reflection map + // + // first figure out where we'll make our 2D guess from + vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + // get attributes from the 2D guess point + float refdepth = texture2DRect(depthMap, ref2d).a; + vec3 refpos = getPosition_d(ref2d, refdepth).xyz; + vec3 refcol = texture2DRect(diffuseRect, ref2d).rgb; + vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); + // figure out how appropriate our guess actually was + float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); + // darken reflections from points which face away from the reflected ray - our guess was a back-face + //refapprop *= step(dot(refnorm, refn), 0.0); + refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant + // get appropriate light strength for guess-point + float reflit = max(dot(refn, vary_light.xyz), 0.0); + // apply sun color to guess-point, dampen according to inappropriateness of guess + vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; + vec3 ssshiny = (refprod * spec.a); + + // add the two types of shiny together + col += (ssshiny + dumbshiny) * spec.rgb; } col = atmosLighting(col); diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 589a292751..c5530c5f59 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -55,9 +55,8 @@ vec3 vary_AmblitColor; vec3 vary_AdditiveColor; vec3 vary_AtmosAttenuation; -vec4 getPosition(vec2 pos_screen) -{ //get position in screen space (world units) given window coordinate and depth map - float depth = texture2DRect(depthMap, pos_screen.xy).a; +vec4 getPosition_d(vec2 pos_screen, float depth) +{ vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); @@ -68,6 +67,12 @@ vec4 getPosition(vec2 pos_screen) return pos; } +vec4 getPosition(vec2 pos_screen) +{ //get position in screen space (world units) given window coordinate and depth map + float depth = texture2DRect(depthMap, pos_screen.xy).a; + return getPosition_d(pos_screen, depth); +} + vec3 getPositionEye() { return vary_PositionEye; @@ -251,7 +256,8 @@ vec3 scaleSoftClip(vec3 light) void main() { vec2 tc = vary_fragcoord.xy; - vec3 pos = getPosition(tc).xyz; + float depth = texture2DRect(depthMap, tc.xy).a; + vec3 pos = getPosition_d(tc, depth).xyz; vec3 norm = texture2DRect(normalMap, tc).xyz*2.0-1.0; //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz; @@ -271,11 +277,37 @@ void main() col *= diffuse.rgb; - if (spec.a > 0.0) + if (spec.a > 0.0) // specular reflection { - vec3 ref = normalize(reflect(pos.xyz, norm.xyz)); - float sa = dot(ref, vary_light.xyz); - col.rgb += vary_SunlitColor*scol_ambocc.r*spec.rgb*texture2D(lightFunc, vec2(sa, spec.a)).a; + // the old infinite-sky shiny reflection + // + vec3 refnorm = normalize(reflect(pos.xyz, norm.xyz)); + float sa = dot(refnorm, vary_light.xyz); + vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; + + // screen-space cheap fakey reflection map + // + // first figure out where we'll make our 2D guess from + vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + // get attributes from the 2D guess point + float refdepth = texture2DRect(depthMap, ref2d).a; + vec3 refpos = getPosition_d(ref2d, refdepth).xyz; + vec3 refcol = texture2DRect(diffuseRect, ref2d).rgb; + float refshad = texture2DRect(lightMap, ref2d).r; + vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); + // figure out how appropriate our guess actually was + float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); + // darken reflections from points which face away from the reflected ray - our guess was a back-face + //refapprop *= step(dot(refnorm, refn), 0.0); + refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant + // get appropriate light strength for guess-point + float reflit = min(max(dot(refn, vary_light.xyz), 0.0), refshad); + // apply sun color to guess-point, dampen according to inappropriateness of guess + vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; + vec3 ssshiny = (refprod * spec.a); + + // add the two types of shiny together + col += (ssshiny + dumbshiny) * spec.rgb; } col = atmosLighting(col); diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 96a083b522..265fb437ec 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -56,9 +56,8 @@ vec3 vary_AdditiveColor; vec3 vary_AtmosAttenuation; uniform float gi_ambiance; -vec4 getPosition(vec2 pos_screen) -{ //get position in screen space (world units) given window coordinate and depth map - float depth = texture2DRect(depthMap, pos_screen.xy).a; +vec4 getPosition_d(vec2 pos_screen, float depth) +{ vec2 sc = pos_screen.xy*2.0; sc /= screen_res; sc -= vec2(1.0,1.0); @@ -69,6 +68,12 @@ vec4 getPosition(vec2 pos_screen) return pos; } +vec4 getPosition(vec2 pos_screen) +{ //get position in screen space (world units) given window coordinate and depth map + float depth = texture2DRect(depthMap, pos_screen.xy).a; + return getPosition_d(pos_screen, depth); +} + vec3 getPositionEye() { return vary_PositionEye; @@ -252,7 +257,8 @@ vec3 scaleSoftClip(vec3 light) void main() { vec2 tc = vary_fragcoord.xy; - vec3 pos = getPosition(tc).xyz; + float depth = texture2DRect(depthMap, tc.xy).a; + vec3 pos = getPosition_d(tc, depth).xyz; vec3 norm = texture2DRect(normalMap, tc).xyz*2.0-1.0; //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz; @@ -274,11 +280,37 @@ void main() col *= diffuse.rgb; - if (spec.a > 0.0) + if (spec.a > 0.0) // specular reflection { - vec3 ref = normalize(reflect(pos.xyz, norm.xyz)); - float sa = dot(ref, vary_light.xyz); - col.rgb += vary_SunlitColor*scol*spec.rgb*texture2D(lightFunc, vec2(sa, spec.a)).a; + // the old infinite-sky shiny reflection + // + vec3 refnorm = normalize(reflect(pos.xyz, norm.xyz)); + float sa = dot(refnorm, vary_light.xyz); + vec3 dumbshiny = vary_SunlitColor*scol*texture2D(lightFunc, vec2(sa, spec.a)).a; + + // screen-space cheap fakey reflection map + // + // first figure out where we'll make our 2D guess from + vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + // get attributes from the 2D guess point + float refdepth = texture2DRect(depthMap, ref2d).a; + vec3 refpos = getPosition_d(ref2d, refdepth).xyz; + vec3 refcol = texture2DRect(diffuseRect, ref2d).rgb; + float refshad = texture2DRect(lightMap, ref2d).r; + vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); + // figure out how appropriate our guess actually was + float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); + // darken reflections from points which face away from the reflected ray - our guess was a back-face + //refapprop *= step(dot(refnorm, refn), 0.0); + refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant + // get appropriate light strength for guess-point + float reflit = min(max(dot(refn, vary_light.xyz), 0.0), refshad); + // apply sun color to guess-point, dampen according to inappropriateness of guess + vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; + vec3 ssshiny = (refprod * spec.a); + + // add the two types of shiny together + col += (ssshiny + dumbshiny) * spec.rgb; } col = atmosLighting(col); -- cgit v1.3 From 122308205b2958b27152b9dd5375554ef76dd084 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 26 Mar 2010 09:07:08 +0000 Subject: screen-space reflections: remember to unbias the value from the depth map. don't use varying version of lightnorm for lighting the reflection-guess-point. --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 3 ++- indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 3 ++- indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 7d57eaa13d..c78d7ece7e 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -288,6 +288,7 @@ void main() // screen-space cheap fakey reflection map // + depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; // get attributes from the 2D guess point @@ -301,7 +302,7 @@ void main() //refapprop *= step(dot(refnorm, refn), 0.0); refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant // get appropriate light strength for guess-point - float reflit = max(dot(refn, vary_light.xyz), 0.0); + float reflit = max(dot(refn, lightnorm.xyz), 0.0); // apply sun color to guess-point, dampen according to inappropriateness of guess vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; vec3 ssshiny = (refprod * spec.a); diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index c5530c5f59..9305d77ddb 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -287,6 +287,7 @@ void main() // screen-space cheap fakey reflection map // + depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; // get attributes from the 2D guess point @@ -301,7 +302,7 @@ void main() //refapprop *= step(dot(refnorm, refn), 0.0); refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant // get appropriate light strength for guess-point - float reflit = min(max(dot(refn, vary_light.xyz), 0.0), refshad); + float reflit = min(max(dot(refn, lightnorm.xyz), 0.0), refshad); // apply sun color to guess-point, dampen according to inappropriateness of guess vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; vec3 ssshiny = (refprod * spec.a); diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 265fb437ec..262b34ede7 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -290,6 +290,7 @@ void main() // screen-space cheap fakey reflection map // + depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; // get attributes from the 2D guess point @@ -304,7 +305,7 @@ void main() //refapprop *= step(dot(refnorm, refn), 0.0); refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant // get appropriate light strength for guess-point - float reflit = min(max(dot(refn, vary_light.xyz), 0.0), refshad); + float reflit = min(max(dot(refn, lightnorm.xyz), 0.0), refshad); // apply sun color to guess-point, dampen according to inappropriateness of guess vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; vec3 ssshiny = (refprod * spec.a); -- cgit v1.3 From 79d2c59112213151060b928a2528c481cfde09fe Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Sun, 28 Mar 2010 21:45:32 +0100 Subject: ss reflections: make the guess less far from the source. --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 2 +- indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index c78d7ece7e..e14869af9e 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -290,7 +290,7 @@ void main() // depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from - vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + vec2 ref2d = tc.xy + (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 9305d77ddb..78e4c179d7 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -289,7 +289,7 @@ void main() // depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from - vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + vec2 ref2d = tc.xy + (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 262b34ede7..9ecb0a40ff 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -292,7 +292,7 @@ void main() // depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from - vec2 ref2d = tc.xy + (0.5 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + vec2 ref2d = tc.xy + (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; -- cgit v1.3 From ccd5b9e2f3f1139814a63d9db03c3671c249930c Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Mon, 29 Mar 2010 11:30:08 +0100 Subject: ss reflections tweakage: + // Offset the guess source a little according to a trivial + // checkerboard dither function and spec.a. + // This is meant to be similar to sampling a blurred version + // of the diffuse map. LOD would be better in that regard. + // The goal of the blur is to soften reflections in surfaces + // with low shinyness, and also to disguise our lameness. --- .../app_settings/shaders/class1/deferred/softenLightF.glsl | 8 ++++++++ .../app_settings/shaders/class2/deferred/softenLightF.glsl | 8 ++++++++ .../app_settings/shaders/class3/deferred/softenLightF.glsl | 8 ++++++++ 3 files changed, 24 insertions(+) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index e14869af9e..4646bd4d40 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -291,6 +291,14 @@ void main() depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from vec2 ref2d = tc.xy + (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + // Offset the guess source a little according to a trivial + // checkerboard dither function and spec.a. + // This is meant to be similar to sampling a blurred version + // of the diffuse map. LOD would be better in that regard. + // The goal of the blur is to soften reflections in surfaces + // with low shinyness, and also to disguise our lameness. + float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 + ref2d += normalize(ref2d)*14.0*(1.0-spec.a)*(checkerboard-0.5); // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 78e4c179d7..5769288a36 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -290,6 +290,14 @@ void main() depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from vec2 ref2d = tc.xy + (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + // Offset the guess source a little according to a trivial + // checkerboard dither function and spec.a. + // This is meant to be similar to sampling a blurred version + // of the diffuse map. LOD would be better in that regard. + // The goal of the blur is to soften reflections in surfaces + // with low shinyness, and also to disguise our lameness. + float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 + ref2d += normalize(ref2d)*14.0*(1.0-spec.a)*(checkerboard-0.5); // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 9ecb0a40ff..70a1c53456 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -293,6 +293,14 @@ void main() depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from vec2 ref2d = tc.xy + (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + // Offset the guess source a little according to a trivial + // checkerboard dither function and spec.a. + // This is meant to be similar to sampling a blurred version + // of the diffuse map. LOD would be better in that regard. + // The goal of the blur is to soften reflections in surfaces + // with low shinyness, and also to disguise our lameness. + float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 + ref2d += normalize(ref2d)*14.0*(1.0-spec.a)*(checkerboard-0.5); // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; -- cgit v1.3 From 784e83462b19c0fbea6656e2a845a1f3ab4b1e70 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Wed, 31 Mar 2010 18:23:42 +0100 Subject: debug ssreflection blur direction. minor. --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 4646bd4d40..155f03fdcf 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -290,7 +290,7 @@ void main() // depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from - vec2 ref2d = tc.xy + (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; + vec2 ref2d = (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; // Offset the guess source a little according to a trivial // checkerboard dither function and spec.a. // This is meant to be similar to sampling a blurred version @@ -299,6 +299,7 @@ void main() // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 ref2d += normalize(ref2d)*14.0*(1.0-spec.a)*(checkerboard-0.5); + ref2d += tc.xy; // use as offset from destination // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; -- cgit v1.3 From 89ad33a53ef239adf332532fa1039ae6313f5ba9 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 2 Apr 2010 14:40:15 +0100 Subject: ssreflections: reflect sunlight direction for reflection. dunno how I feel about this yet. --- .../newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 155f03fdcf..51d737f10b 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -310,8 +310,11 @@ void main() // darken reflections from points which face away from the reflected ray - our guess was a back-face //refapprop *= step(dot(refnorm, refn), 0.0); refapprop = min(refapprop, max(0.0, -dot(refnorm, refn))); // more conservative variant - // get appropriate light strength for guess-point - float reflit = max(dot(refn, lightnorm.xyz), 0.0); + // get appropriate light strength for guess-point. + // reflect light direction to increase the illusion that + // these are reflections. + vec3 reflight = reflect(lightnorm.xyz, norm.xyz); + float reflit = max(dot(refn, reflight.xyz), 0.0); // apply sun color to guess-point, dampen according to inappropriateness of guess vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; vec3 ssshiny = (refprod * spec.a); -- cgit v1.3 From cae24385d7a119d0ad634d252bd3bb1e4e68c57c Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 2 Apr 2010 14:50:24 +0100 Subject: ssreflections: use the minimum of the lighting dp and the appropriateness. not the product. --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 51d737f10b..8b2b983ba4 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -317,6 +317,8 @@ void main() float reflit = max(dot(refn, reflight.xyz), 0.0); // apply sun color to guess-point, dampen according to inappropriateness of guess vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; + float refmod = min(refapprop, reflit); + vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); // add the two types of shiny together -- cgit v1.3 From 145ffe0da00f87ad16eb17dcec3c5a1a201fe32b Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 2 Apr 2010 15:20:35 +0100 Subject: ssreflections: water reflections strongly imply that the non-perspective reflection from eye is the right one. --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 8b2b983ba4..25733e80d1 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -282,7 +282,8 @@ void main() { // the old infinite-sky shiny reflection // - vec3 refnorm = normalize(reflect(pos.xyz, norm.xyz)); + vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz)); + //vec3 refnorm = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(refnorm, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; -- cgit v1.3 From 7261a23f39ce2dbfadf52427ce693896d9d24bf0 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 2 Apr 2010 15:46:47 +0100 Subject: ssreflections: perspective reflection for sunlight, planar reflection for ssreflection --- .../newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 25733e80d1..d00d3ed412 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -282,13 +282,13 @@ void main() { // the old infinite-sky shiny reflection // - vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz)); - //vec3 refnorm = normalize(reflect(pos.xyz, norm.xyz)); - float sa = dot(refnorm, vary_light.xyz); + vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); + float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; // screen-space cheap fakey reflection map // + vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz)); depth -= 0.5; // unbias depth // first figure out where we'll make our 2D guess from vec2 ref2d = (0.25 * screen_res.y) * (refnorm.xy) * abs(refnorm.z) / depth; -- cgit v1.3 From 35d92b2db91a9da1ae240f14202195c36a2b40bf Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 2 Apr 2010 15:59:11 +0100 Subject: clean-up transplant --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 1 - 1 file changed, 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index d00d3ed412..503c7d0b83 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -317,7 +317,6 @@ void main() vec3 reflight = reflect(lightnorm.xyz, norm.xyz); float reflit = max(dot(refn, reflight.xyz), 0.0); // apply sun color to guess-point, dampen according to inappropriateness of guess - vec3 refprod = (vary_SunlitColor*reflit) * refcol.rgb * refapprop; float refmod = min(refapprop, reflit); vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); -- cgit v1.3 From 3f05cd7e7fdb7da83f27f798913a0b0b848a08a6 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Tue, 6 Apr 2010 22:24:57 +0100 Subject: extra diffuse sample in ssreflection to decrease aliasing. --- .../app_settings/shaders/class1/deferred/softenLightF.glsl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 503c7d0b83..3f4425a0b2 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -298,13 +298,18 @@ void main() // of the diffuse map. LOD would be better in that regard. // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. + // --------------------- + // ^ ^ ^ ^ ^ + // a . b o c . d check=0:avg(a,b) check=1:avg(c,d) float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - ref2d += normalize(ref2d)*14.0*(1.0-spec.a)*(checkerboard-0.5); + vec2 checkoffset = normalize(ref2d)*5.0*(1.0-spec.a)*(checkerboard-0.5); + ref2d += checkoffset; ref2d += tc.xy; // use as offset from destination // get attributes from the 2D guess point float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; - vec3 refcol = texture2DRect(diffuseRect, ref2d).rgb; + vec3 refcol = 0.5 * (texture2DRect(diffuseRect, ref2d).rgb + + texture2DRect(diffuseRect, ref2d + checkoffset*2.0).rgb); vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); // figure out how appropriate our guess actually was float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); -- cgit v1.3 From 9454398cd93f1b9710ef027206180a605c374dbd Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Wed, 7 Apr 2010 09:45:20 +0100 Subject: tidy up ssreflections comments and structure a bit. --- .../app_settings/shaders/class1/deferred/softenLightF.glsl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 3f4425a0b2..158eef9319 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -298,18 +298,20 @@ void main() // of the diffuse map. LOD would be better in that regard. // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. - // --------------------- - // ^ ^ ^ ^ ^ - // a . b o c . d check=0:avg(a,b) check=1:avg(c,d) float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 vec2 checkoffset = normalize(ref2d)*5.0*(1.0-spec.a)*(checkerboard-0.5); ref2d += checkoffset; ref2d += tc.xy; // use as offset from destination - // get attributes from the 2D guess point - float refdepth = texture2DRect(depthMap, ref2d).a; - vec3 refpos = getPosition_d(ref2d, refdepth).xyz; + // Get attributes from the 2D guess point. + // We average two samples of diffuse (not of anything else) per + // pixel to try to reduce aliasing some more. + // --------------------- + // ^ ^ ^ ^ ^ + // a . b o c . d check=0:avg(a,b) check=1:avg(c,d) vec3 refcol = 0.5 * (texture2DRect(diffuseRect, ref2d).rgb + texture2DRect(diffuseRect, ref2d + checkoffset*2.0).rgb); + float refdepth = texture2DRect(depthMap, ref2d).a; + vec3 refpos = getPosition_d(ref2d, refdepth).xyz; vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); // figure out how appropriate our guess actually was float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); -- cgit v1.3 From 6b72149cf7ad90c0036d9b037594b0a8e86dc483 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Mon, 12 Apr 2010 23:15:42 -0500 Subject: Tracking down ATI deferred bugs WIP --- indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl | 4 ++-- indra/newview/lldrawpoolavatar.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl index 00083eb6b3..085ffddeec 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarShadowF.glsl @@ -10,7 +10,7 @@ uniform sampler2D diffuseMap; void main() { - gl_FragColor = vec4(1,1,1,gl_Color.a * texture2D(diffuseMap, gl_TexCoord[0].xy).a); - //gl_FragColor = vec4(1,1,1,1); + //gl_FragColor = vec4(1,1,1,gl_Color.a * texture2D(diffuseMap, gl_TexCoord[0].xy).a); + gl_FragColor = vec4(1,1,1,1); } diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 23211d7090..5a67e1753a 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -254,7 +254,7 @@ S32 LLDrawPoolAvatar::getNumShadowPasses() void LLDrawPoolAvatar::beginShadowPass(S32 pass) { LLFastTimer t(FTM_SHADOW_AVATAR); - + sSkipTransparent = TRUE; sVertexProgram = &gDeferredAvatarShadowProgram; if (sShaderLevel > 0) { @@ -276,7 +276,7 @@ void LLDrawPoolAvatar::beginShadowPass(S32 pass) void LLDrawPoolAvatar::endShadowPass(S32 pass) { LLFastTimer t(FTM_SHADOW_AVATAR); - + sSkipTransparent = FALSE; if (sShaderLevel > 0) { sRenderingSkinned = FALSE; -- cgit v1.3 From 14f3b77b9c4103f324d24625bd30dd106ab1817f Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 16 Apr 2010 12:19:30 +0100 Subject: strengthen the fakey blur in the fakey ssreflections. (transplanted from 940e02cbe4fa2f996d11500392e71f3a00e1cfed) --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 2 +- indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 2 +- indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 158eef9319..5fb86dd92d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -299,7 +299,7 @@ void main() // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - vec2 checkoffset = normalize(ref2d)*5.0*(1.0-spec.a)*(checkerboard-0.5); + vec2 checkoffset = normalize(ref2d)*9.0*(1.0-spec.a)*(checkerboard-0.5); ref2d += checkoffset; ref2d += tc.xy; // use as offset from destination // Get attributes from the 2D guess point. diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index dbccb7fb8b..1fd54b5607 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -298,7 +298,7 @@ void main() // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - vec2 checkoffset = normalize(ref2d)*5.0*(1.0-spec.a)*(checkerboard-0.5); + vec2 checkoffset = normalize(ref2d)*9.0*(1.0-spec.a)*(checkerboard-0.5); ref2d += checkoffset; ref2d += tc.xy; // use as offset from destination // Get attributes from the 2D guess point. diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index ef81ed1308..45d921d861 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -301,7 +301,7 @@ void main() // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - vec2 checkoffset = normalize(ref2d)*5.0*(1.0-spec.a)*(checkerboard-0.5); + vec2 checkoffset = normalize(ref2d)*9.0*(1.0-spec.a)*(checkerboard-0.5); ref2d += checkoffset; ref2d += tc.xy; // use as offset from destination // Get attributes from the 2D guess point. -- cgit v1.3 From ba294e9ad95e8db491ea19c8a370e88a31814c9d Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Wed, 21 Apr 2010 14:24:44 +0100 Subject: ssreflections: if we're going to (pretend to) take 4 diffuse samples, then take them in a diamond pattern instead of all in a line. this also slightly simplifies(?) the shader. (transplanted from 7b334c22ece4e98565c5d182690ab9ca4c2526c6) --- .../app_settings/shaders/class1/deferred/softenLightF.glsl | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 5fb86dd92d..01d18cdcde 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -299,17 +299,14 @@ void main() // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - vec2 checkoffset = normalize(ref2d)*9.0*(1.0-spec.a)*(checkerboard-0.5); - ref2d += checkoffset; + float checkoffset = 1.0 + (7.0*(1.0-spec.a))*(checkerboard-0.5); + ref2d += vec2(checkoffset, checkoffset); ref2d += tc.xy; // use as offset from destination // Get attributes from the 2D guess point. // We average two samples of diffuse (not of anything else) per // pixel to try to reduce aliasing some more. - // --------------------- - // ^ ^ ^ ^ ^ - // a . b o c . d check=0:avg(a,b) check=1:avg(c,d) - vec3 refcol = 0.5 * (texture2DRect(diffuseRect, ref2d).rgb + - texture2DRect(diffuseRect, ref2d + checkoffset*2.0).rgb); + vec3 refcol = 0.5 * (texture2DRect(diffuseRect, ref2d + vec2(0.0, -checkoffset)).rgb + + texture2DRect(diffuseRect, ref2d + vec2(-checkoffset, 0.0)).rgb); float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); -- cgit v1.3 From 12e0a745dd97b7bd5a10ee01dec3e5d3055b515c Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Thu, 22 Apr 2010 21:33:10 +0100 Subject: ssreflections: dampen/blur ssreflections rather more. --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 3 ++- indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 3 ++- indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 01d18cdcde..bf5dd4155c 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -299,7 +299,7 @@ void main() // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - float checkoffset = 1.0 + (7.0*(1.0-spec.a))*(checkerboard-0.5); + float checkoffset = (3.0 + (7.0*(1.0-spec.a)))*(checkerboard-0.5); ref2d += vec2(checkoffset, checkoffset); ref2d += tc.xy; // use as offset from destination // Get attributes from the 2D guess point. @@ -324,6 +324,7 @@ void main() float refmod = min(refapprop, reflit); vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); + ssshiny *= 0.3; // dampen it even more // add the two types of shiny together col += (ssshiny + dumbshiny) * spec.rgb; diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 2982cd3e09..28cfc6322e 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -298,7 +298,7 @@ void main() // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - float checkoffset = 1.0 + (7.0*(1.0-spec.a))*(checkerboard-0.5); + float checkoffset = (3.0 + (7.0*(1.0-spec.a)))*(checkerboard-0.5); ref2d += vec2(checkoffset, checkoffset); ref2d += tc.xy; // use as offset from destination // Get attributes from the 2D guess point. @@ -324,6 +324,7 @@ void main() float refmod = min(refapprop, reflit); vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); + ssshiny *= 0.3; // dampen it even more // add the two types of shiny together col += (ssshiny + dumbshiny) * spec.rgb; diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index e1e035411b..9f94b9e8ea 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -301,7 +301,8 @@ void main() // The goal of the blur is to soften reflections in surfaces // with low shinyness, and also to disguise our lameness. float checkerboard = floor(mod(tc.x+tc.y, 2.0)); // 0.0, 1.0 - float checkoffset = 1.0 + (7.0*(1.0-spec.a))*(checkerboard-0.5); + float checkoffset = (3.0 + (7.0*(1.0-spec.a)))*(checkerboard-0.5); + ref2d += vec2(checkoffset, checkoffset); ref2d += tc.xy; // use as offset from destination // Get attributes from the 2D guess point. @@ -327,6 +328,7 @@ void main() float refmod = min(refapprop, reflit); vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); + ssshiny *= 0.3; // dampen it even more // add the two types of shiny together col += (ssshiny + dumbshiny) * spec.rgb; -- cgit v1.3 From f972cc4be952abc943f9d289533c9951ce4dc839 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 23 Apr 2010 13:37:03 +0100 Subject: Restore a bit of the non-deferred renderer's sun/moon waterglow in deferred rendering. This also adds a slight pinch of glow to shiny sun-spots. So be it. (transplanted from 5a0d9e5b5cfb2ecd96685f0275ab8e999ab86263) --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 4 +++- indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 4 +++- indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index bf5dd4155c..65540ad6fe 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -270,6 +270,7 @@ void main() vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; float scol = max(scol_ambocc.r, diffuse.a); float ambocc = scol_ambocc.g; + float glowresult = 0.0; calcAtmospherics(pos.xyz, ambocc); @@ -285,6 +286,7 @@ void main() vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; + glowresult = 0.08 * dot(dumbshiny.rgb, spec.rgb); // screen-space cheap fakey reflection map // @@ -334,5 +336,5 @@ void main() col = scaleSoftClip(col); gl_FragColor.rgb = col; - gl_FragColor.a = 0.0; + gl_FragColor.a = glowresult; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 28cfc6322e..67ef3d5280 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -269,6 +269,7 @@ void main() vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; float scol = max(scol_ambocc.r, diffuse.a); float ambocc = scol_ambocc.g; + float glowresult = 0.0; calcAtmospherics(pos.xyz, ambocc); @@ -284,6 +285,7 @@ void main() vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; + glowresult = 0.08 * dot(dumbshiny.rgb, spec.rgb); // screen-space cheap fakey reflection map // @@ -334,5 +336,5 @@ void main() col = scaleSoftClip(col); gl_FragColor.rgb = col; - gl_FragColor.a = 0.0; + gl_FragColor.a = glowresult; } diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 9f94b9e8ea..b5c6693d3b 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -272,6 +272,7 @@ void main() vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; float scol = max(scol_ambocc.r, diffuse.a); float ambocc = scol_ambocc.g; + float glowresult = 0.0; calcAtmospherics(pos.xyz, ambocc); @@ -287,6 +288,7 @@ void main() vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol*texture2D(lightFunc, vec2(sa, spec.a)).a; + glowresult = 0.08 * dot(dumbshiny.rgb, spec.rgb); // screen-space cheap fakey reflection map // @@ -340,7 +342,7 @@ void main() gl_FragColor.rgb = col; //gl_FragColor.rgb = gi_col.rgb; - gl_FragColor.a = 0.0; + gl_FragColor.a = glowresult; //gl_FragColor.rg = scol_ambocc.rg; //gl_FragColor.rgb = texture2DRect(lightMap, vary_fragcoord.xy).rgb; -- cgit v1.3 From f313196363085de2eb8f3a4fd33abeb5617204c4 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 23 Apr 2010 17:35:32 +0100 Subject: Backed out changeset 90f0ce75b2d7 --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 4 +--- indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 4 +--- indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl | 4 +--- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index 65540ad6fe..bf5dd4155c 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -270,7 +270,6 @@ void main() vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; float scol = max(scol_ambocc.r, diffuse.a); float ambocc = scol_ambocc.g; - float glowresult = 0.0; calcAtmospherics(pos.xyz, ambocc); @@ -286,7 +285,6 @@ void main() vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; - glowresult = 0.08 * dot(dumbshiny.rgb, spec.rgb); // screen-space cheap fakey reflection map // @@ -336,5 +334,5 @@ void main() col = scaleSoftClip(col); gl_FragColor.rgb = col; - gl_FragColor.a = glowresult; + gl_FragColor.a = 0.0; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 67ef3d5280..28cfc6322e 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -269,7 +269,6 @@ void main() vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; float scol = max(scol_ambocc.r, diffuse.a); float ambocc = scol_ambocc.g; - float glowresult = 0.0; calcAtmospherics(pos.xyz, ambocc); @@ -285,7 +284,6 @@ void main() vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; - glowresult = 0.08 * dot(dumbshiny.rgb, spec.rgb); // screen-space cheap fakey reflection map // @@ -336,5 +334,5 @@ void main() col = scaleSoftClip(col); gl_FragColor.rgb = col; - gl_FragColor.a = glowresult; + gl_FragColor.a = 0.0; } diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index b5c6693d3b..9f94b9e8ea 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -272,7 +272,6 @@ void main() vec2 scol_ambocc = texture2DRect(lightMap, vary_fragcoord.xy).rg; float scol = max(scol_ambocc.r, diffuse.a); float ambocc = scol_ambocc.g; - float glowresult = 0.0; calcAtmospherics(pos.xyz, ambocc); @@ -288,7 +287,6 @@ void main() vec3 refnormpersp = normalize(reflect(pos.xyz, norm.xyz)); float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol*texture2D(lightFunc, vec2(sa, spec.a)).a; - glowresult = 0.08 * dot(dumbshiny.rgb, spec.rgb); // screen-space cheap fakey reflection map // @@ -342,7 +340,7 @@ void main() gl_FragColor.rgb = col; //gl_FragColor.rgb = gi_col.rgb; - gl_FragColor.a = glowresult; + gl_FragColor.a = 0.0; //gl_FragColor.rg = scol_ambocc.rg; //gl_FragColor.rgb = texture2DRect(lightMap, vary_fragcoord.xy).rgb; -- cgit v1.3 From 4f46e777a501ff0bfedaf657ac6decdeba8acbe5 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 30 Apr 2010 09:45:44 +0100 Subject: EXT-7149 objects with white color and varied texture do not appear shiny with deferred rendering (transplanted from 778a5a83ef4e5e70652fbc00f2e1f8375e587afc) --- indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl index 5895ebda84..59b1720578 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl @@ -11,8 +11,9 @@ varying vec3 vary_normal; void main() { - vec3 col = texture2D(diffuseMap, gl_TexCoord[0].xy).rgb; - gl_FragData[0] = vec4(gl_Color.rgb*col, 0.0); - gl_FragData[1] = vec4(col*(gl_Color.a*1.5), gl_Color.a); + vec3 col = gl_Color.rgb * texture2D(diffuseMap, gl_TexCoord[0].xy).rgb; + gl_FragData[0] = vec4(col, 0.0); + gl_FragData[1] = vec4(gl_Color.aaa*1.5, gl_Color.a); // spec + //gl_FragData[1] = vec4(vec3(gl_Color.a), gl_Color.a+(1.0-gl_Color.a)*gl_Color.a); // spec - from former class3 - maybe better, but not so well tested gl_FragData[2] = vec4(normalize(vary_normal)*0.5+0.5, 0.0); } -- cgit v1.3 From 41afe80984ee6d0363032193ea4a5d3eb867b3b1 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 30 Apr 2010 10:40:27 +0100 Subject: more tweakery for EXT-7149 objects with white color and varied texture do not appear shiny with deferred rendering (transplanted from 36680184ec50ecf5fff92ee33db711b7512d923a) --- indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl index 59b1720578..112103956d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl @@ -13,7 +13,7 @@ void main() { vec3 col = gl_Color.rgb * texture2D(diffuseMap, gl_TexCoord[0].xy).rgb; gl_FragData[0] = vec4(col, 0.0); - gl_FragData[1] = vec4(gl_Color.aaa*1.5, gl_Color.a); // spec + gl_FragData[1] = gl_Color.aaaa; // spec //gl_FragData[1] = vec4(vec3(gl_Color.a), gl_Color.a+(1.0-gl_Color.a)*gl_Color.a); // spec - from former class3 - maybe better, but not so well tested gl_FragData[2] = vec4(normalize(vary_normal)*0.5+0.5, 0.0); } -- cgit v1.3 From cf258fa9defe5559678aa8417233f0e7882b7c32 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Fri, 30 Apr 2010 11:58:45 +0100 Subject: expand EXT-7149 fix to include bumpyshiny. (transplanted from 57e136beedee9122dbbf46a1d91f6514f058b829) --- indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl index 1c29dae5f7..6e38caf5ef 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl @@ -14,14 +14,15 @@ varying vec3 vary_mat2; void main() { - vec3 col = texture2D(diffuseMap, gl_TexCoord[0].xy).rgb; + vec3 col = gl_Color.rgb * texture2D(diffuseMap, gl_TexCoord[0].xy).rgb; vec3 norm = texture2D(bumpMap, gl_TexCoord[0].xy).rgb * 2.0 - 1.0; vec3 tnorm = vec3(dot(norm,vary_mat0), - dot(norm,vary_mat1), - dot(norm,vary_mat2)); + dot(norm,vary_mat1), + dot(norm,vary_mat2)); - gl_FragData[0] = vec4(gl_Color.rgb*col, 0.0); - gl_FragData[1] = vec4(col*gl_Color.a, gl_Color.a); + gl_FragData[0] = vec4(col, 0.0); + gl_FragData[1] = gl_Color.aaaa; // spec + //gl_FragData[1] = vec4(vec3(gl_Color.a), gl_Color.a+(1.0-gl_Color.a)*gl_Color.a); // spec - from former class3 - maybe better, but not so well tested gl_FragData[2] = vec4(normalize(tnorm)*0.5+0.5, 0.0); } -- cgit v1.3 From f324787a70b8fccc7a8ea202805bf726fba765a7 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Sat, 1 May 2010 00:45:44 -0500 Subject: Rigged attachment integration WIP. --- indra/llrender/llglslshader.cpp | 2 +- .../shaders/class1/deferred/diffuseV.glsl | 2 +- indra/newview/lldrawpool.cpp | 5 - indra/newview/lldrawpool.h | 2 - indra/newview/lldrawpoolavatar.cpp | 244 ++++++++++++++++++++- indra/newview/lldrawpoolavatar.h | 42 +++- indra/newview/lldrawpoolbump.cpp | 54 +++-- indra/newview/lldrawpoolbump.h | 4 + indra/newview/llface.cpp | 5 + indra/newview/llface.h | 1 + indra/newview/llviewerobject.cpp | 19 ++ indra/newview/llviewerobject.h | 1 + indra/newview/llviewershadermgr.cpp | 27 ++- indra/newview/llviewershadermgr.h | 1 + indra/newview/llvoavatar.cpp | 99 +-------- indra/newview/llvovolume.cpp | 66 +++++- 16 files changed, 440 insertions(+), 134 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/llrender/llglslshader.cpp b/indra/llrender/llglslshader.cpp index 949057df04..9256e3959c 100644 --- a/indra/llrender/llglslshader.cpp +++ b/indra/llrender/llglslshader.cpp @@ -124,7 +124,7 @@ BOOL LLGLSLShader::createShader(vector * attributes, { GLhandleARB shaderhandle = LLShaderMgr::instance()->loadShaderFile((*fileIter).first, mShaderLevel, (*fileIter).second); LL_DEBUGS("ShaderLoading") << "SHADER FILE: " << (*fileIter).first << " mShaderLevel=" << mShaderLevel << LL_ENDL; - if (mShaderLevel > 0) + if (shaderhandle > 0) { attachObject(shaderhandle); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl index 44468cdfa2..b458842657 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl @@ -13,7 +13,7 @@ void main() gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; - vary_normal = normalize(gl_NormalMatrix * gl_Normal); + vary_nomral = normalize(gl_NormalMatrix * gl_Normal); gl_FrontColor = gl_Color; } diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp index ae30af3647..a99b80d618 100644 --- a/indra/newview/lldrawpool.cpp +++ b/indra/newview/lldrawpool.cpp @@ -248,11 +248,6 @@ void LLFacePool::dirtyTextures(const std::set& textures { } -BOOL LLFacePool::moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data) -{ - return TRUE; -} - // static S32 LLFacePool::drawLoop(face_array_t& face_list) { diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h index 67870c10e9..e46d503db3 100644 --- a/indra/newview/lldrawpool.h +++ b/indra/newview/lldrawpool.h @@ -187,8 +187,6 @@ public: virtual void resetDrawOrders(); void resetAll(); - BOOL moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data = FALSE); - void destroy(); void buildEdges(); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 62226383a4..02c7e3bb6f 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -39,13 +39,17 @@ #include "m3math.h" #include "lldrawable.h" +#include "lldrawpoolbump.h" #include "llface.h" +#include "llmeshrepository.h" #include "llsky.h" #include "llviewercamera.h" #include "llviewerregion.h" #include "noise.h" #include "pipeline.h" #include "llviewershadermgr.h" +#include "llvovolume.h" +#include "llvolume.h" #include "llappviewer.h" #include "llrendersphere.h" #include "llviewerpartsim.h" @@ -94,6 +98,8 @@ static BOOL sRenderingSkinned = FALSE; S32 normal_channel = -1; S32 specular_channel = -1; S32 diffuse_channel = -1; +S32 cube_channel = -1; + static LLFastTimer::DeclareTimer FTM_SHADOW_AVATAR("Avatar Shadow"); @@ -358,7 +364,7 @@ S32 LLDrawPoolAvatar::getNumPasses() } else if (getVertexShaderLevel() > 0) { - return 4; + return 5; } else { @@ -402,7 +408,10 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass) beginSkinned(); break; case 3: - beginRigged(); + beginRiggedSimple(); + break; + case 4: + beginRiggedShinySimple(); break; } } @@ -429,7 +438,10 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass) endSkinned(); break; case 3: - endRigged(); + endRiggedSimple(); + break; + case 4: + endRiggedShinySimple(); break; } } @@ -616,14 +628,15 @@ void LLDrawPoolAvatar::endSkinned() gGL.getTexUnit(0)->activate(); } -void LLDrawPoolAvatar::beginRigged() +void LLDrawPoolAvatar::beginRiggedSimple() { sVertexProgram = &gSkinnedObjectSimpleProgram; + diffuse_channel = 0; gSkinnedObjectSimpleProgram.bind(); LLVertexBuffer::sWeight4Loc = gSkinnedObjectSimpleProgram.getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); } -void LLDrawPoolAvatar::endRigged() +void LLDrawPoolAvatar::endRiggedSimple() { sVertexProgram = NULL; LLVertexBuffer::unbind(); @@ -631,6 +644,23 @@ void LLDrawPoolAvatar::endRigged() LLVertexBuffer::sWeight4Loc = -1; } +void LLDrawPoolAvatar::beginRiggedShinySimple() +{ + sVertexProgram = &gSkinnedObjectShinySimpleProgram; + sVertexProgram->bind(); + LLDrawPoolBump::bindCubeMap(sVertexProgram, 2, diffuse_channel, cube_channel, false); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); +} + +void LLDrawPoolAvatar::endRiggedShinySimple() +{ + LLVertexBuffer::unbind(); + LLDrawPoolBump::unbindCubeMap(sVertexProgram, 2, diffuse_channel, cube_channel, false); + sVertexProgram->unbind(); + sVertexProgram = NULL; + LLVertexBuffer::sWeight4Loc = -1; +} + void LLDrawPoolAvatar::beginDeferredRigged() { sVertexProgram = &gDeferredSkinnedDiffuseProgram; @@ -790,9 +820,16 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) if (pass == 3) { - avatarp->renderSkinnedAttachments(); + renderRiggedSimple(avatarp); + return; + } + + if (pass == 4) + { + renderRiggedShinySimple(avatarp); return; } + if (sShaderLevel > 0) { @@ -830,13 +867,146 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) } } +void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face, U32 data_mask) +{ + LLVertexBuffer* buff = face->mVertexBuffer; + + if (!buff || + !buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) || + buff->getRequestedVerts() != vol_face.mVertices.size()) + { + face->setGeomIndex(0); + face->setIndicesIndex(0); + face->setSize(vol_face.mVertices.size(), vol_face.mIndices.size()); + + face->mVertexBuffer = new LLVertexBuffer(data_mask, 0); + face->mVertexBuffer->allocateBuffer(vol_face.mVertices.size(), vol_face.mIndices.size(), true); + + U16 offset = 0; + + LLMatrix4 mat_vert = skin->mBindShapeMatrix; + glh::matrix4f m((F32*) mat_vert.mMatrix); + m = m.inverse().transpose(); + + F32 mat3[] = + { m.m[0], m.m[1], m.m[2], + m.m[4], m.m[5], m.m[6], + m.m[8], m.m[9], m.m[10] }; + + LLMatrix3 mat_normal(mat3); + + face->getGeometryVolume(*volume, face->getTEOffset(), mat_vert, mat_normal, offset, true); + buff = face->mVertexBuffer; + } +} + +void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, const U32 data_mask) +{ + for (U32 i = 0; i < mRiggedFace[type].size(); ++i) + { + LLFace* face = mRiggedFace[type][i]; + LLDrawable* drawable = face->getDrawable(); + if (!drawable) + { + continue; + } + + LLVOVolume* vobj = drawable->getVOVolume(); + + if (!vobj) + { + continue; + } + + LLVolume* volume = vobj->getVolume(); + S32 te = face->getTEOffset(); + + if (!volume || volume->getNumVolumeFaces() <= te) + { + continue; + } + + LLUUID mesh_id = volume->getParams().getSculptID(); + if (mesh_id.isNull()) + { + continue; + } + + const LLMeshSkinInfo* skin = gMeshRepo.getSkinInfo(mesh_id); + if (!skin) + { + continue; + } + + const LLVolumeFace& vol_face = volume->getVolumeFace(te); + updateRiggedFaceVertexBuffer(face, skin, volume, vol_face, data_mask); + + LLVertexBuffer* buff = face->mVertexBuffer; + + if (buff) + { + LLMatrix4 mat[64]; + + for (U32 i = 0; i < skin->mJointNames.size(); ++i) + { + LLJoint* joint = avatar->getJoint(skin->mJointNames[i]); + if (joint) + { + mat[i] = skin->mInvBindMatrix[i]; + mat[i] *= joint->getWorldMatrix(); + } + } + + LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv("matrixPalette", + skin->mJointNames.size(), + FALSE, + (GLfloat*) mat[0].mMatrix); + LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv("matrixPalette[0]", + skin->mJointNames.size(), + FALSE, + (GLfloat*) mat[0].mMatrix); + + buff->setBuffer(data_mask); + + U16 start = face->getGeomStart(); + U16 end = start + face->getGeomCount()-1; + S32 offset = face->getIndicesStart(); + U32 count = face->getIndicesCount(); + + gGL.getTexUnit(0)->bind(face->getTexture()); + buff->drawRange(LLRender::TRIANGLES, start, end, count, offset); + } + } +} + +void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar) +{ + const U32 data_mask = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4; + + renderRigged(avatar, RIGGED_SIMPLE, data_mask); +} + + +void LLDrawPoolAvatar::renderRiggedShinySimple(LLVOAvatar* avatar) +{ + const U32 data_mask = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4; + + renderRigged(avatar, RIGGED_SHINY_SIMPLE, data_mask); +} + //----------------------------------------------------------------------------- // renderForSelect() //----------------------------------------------------------------------------- void LLDrawPoolAvatar::renderForSelect() { - - if (mDrawFace.empty()) { return; @@ -930,6 +1100,64 @@ LLColor3 LLDrawPoolAvatar::getDebugColor() const return LLColor3(0.f, 1.f, 0.f); } +void LLDrawPoolAvatar::addRiggedFace(LLFace* facep, U32 type) +{ + if (facep->getReferenceIndex() != -1) + { + llerrs << "Tried to add a rigged face that's referenced elsewhere." << llendl; + } + + if (type >= NUM_RIGGED_PASSES) + { + llerrs << "Invalid rigged face type." << llendl; + } + + facep->setReferenceIndex(mRiggedFace[type].size()); + facep->mDrawPoolp = this; + mRiggedFace[type].push_back(facep); +} + +void LLDrawPoolAvatar::removeRiggedFace(LLFace* facep, U32 type) +{ + S32 index = facep->getReferenceIndex(); + if (index == -1) + { + llerrs << "Tried to remove rigged face with invalid index." << llendl; + } + + if (type > RIGGED_UNKNOWN) + { + llerrs << "Invalid rigged face type." << llendl; + } + + facep->setReferenceIndex(-1); + facep->mDrawPoolp = NULL; + + if (type == RIGGED_UNKNOWN) + { + for (U32 i = 0; i < NUM_RIGGED_PASSES; ++i) + { + if (mRiggedFace[i].size() > index && mRiggedFace[i][index] == facep) + { + type = i; + break; + } + } + } + + if (type >= NUM_RIGGED_PASSES) + { + llerrs << "Could not find face for removal from current drawpool." << llendl; + } + + mRiggedFace[type].erase(mRiggedFace[type].begin()+index); + + for (S32 i = index; i < mRiggedFace[type].size(); ++i) + { //bump indexes of currently held faces down after removal + mRiggedFace[type][i]->setReferenceIndex(i); + } +} + LLVertexBufferAvatar::LLVertexBufferAvatar() : LLVertexBuffer(sDataMask, GL_STREAM_DRAW_ARB) //avatars are always stream draw due to morph targets diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index b42cc54622..0ebb035f2a 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -37,6 +37,11 @@ class LLVOAvatar; class LLGLSLShader; +class LLFace; +class LLMeshSkinInfo; +class LLVolume; +class LLVolumeFace; + class LLDrawPoolAvatar : public LLFacePool { @@ -91,12 +96,14 @@ public: void beginRigid(); void beginImpostor(); void beginSkinned(); - void beginRigged(); - + void beginRiggedSimple(); + void beginRiggedShinySimple(); + void endRigid(); void endImpostor(); void endSkinned(); - void endRigged(); + void endRiggedSimple(); + void endRiggedShinySimple(); void beginDeferredImpostor(); void beginDeferredRigid(); @@ -108,11 +115,40 @@ public: void endDeferredSkinned(); void endDeferredRigged(); + void updateRiggedFaceVertexBuffer(LLFace* facep, + const LLMeshSkinInfo* skin, + LLVolume* volume, + const LLVolumeFace& vol_face, + U32 data_mask); + + void renderRigged(LLVOAvatar* avatar, U32 type, const U32 data_mask); + void renderRiggedSimple(LLVOAvatar* avatar); + void renderRiggedShinySimple(LLVOAvatar* avatar); + /*virtual*/ LLViewerTexture *getDebugTexture(); /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display void renderAvatars(LLVOAvatar *single_avatar, S32 pass = -1); // renders only one avatar if single_avatar is not null. + typedef enum + { + RIGGED_SIMPLE = 0, + RIGGED_SHINY_SIMPLE, + RIGGED_SHINY_FULLBRIGHT, + RIGGED_SHINY_BUMP, + RIGGED_BUMP, + RIGGED_FULLBRIGHT, + RIGGED_ALPHA, + NUM_RIGGED_PASSES, + RIGGED_UNKNOWN, + } eRiggedPass; + + + void addRiggedFace(LLFace* facep, U32 type); + void removeRiggedFace(LLFace* facep, U32 type = RIGGED_UNKNOWN); + + std::vector mRiggedFace[NUM_RIGGED_PASSES]; + static BOOL sSkipOpaque; static BOOL sSkipTransparent; static LLGLSLShader* sVertexProgram; diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 8f3e775976..2f449fa42f 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -323,30 +323,43 @@ void LLDrawPoolBump::beginShiny(bool invisible) sVertexMask = VERTEX_MASK_SHINY | LLVertexBuffer::MAP_TEXCOORD0; } - if (LLPipeline::sUnderWaterRender) + if (getVertexShaderLevel() > 0) { - shader = &gObjectShinyWaterProgram; + if (LLPipeline::sUnderWaterRender) + { + shader = &gObjectShinyWaterProgram; + } + else + { + shader = &gObjectShinyProgram; + } + shader->bind(); } else { - shader = &gObjectShinyProgram; + shader = NULL; } + bindCubeMap(shader, mVertexShaderLevel, diffuse_channel, cube_channel, invisible); +} + +//static +void LLDrawPoolBump::bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible) +{ LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; if( cube_map ) { - if (!invisible && LLViewerShaderMgr::instance()->getVertexShaderLevel(LLViewerShaderMgr::SHADER_OBJECT) > 0 ) + if (!invisible && shader ) { LLMatrix4 mat; mat.initRows(LLVector4(gGLModelView+0), LLVector4(gGLModelView+4), LLVector4(gGLModelView+8), LLVector4(gGLModelView+12)); - shader->bind(); LLVector3 vec = LLVector3(gShinyOrigin) * mat; LLVector4 vec4(vec, gShinyOrigin.mV[3]); shader->uniform4fv(LLViewerShaderMgr::SHINY_ORIGIN, 1, vec4.mV); - if (mVertexShaderLevel > 1) + if (shader_level > 1) { cube_map->setMatrix(1); // Make sure that texture coord generation happens for tex unit 1, as that's the one we use for @@ -408,22 +421,16 @@ void LLDrawPoolBump::renderShiny(bool invisible) } } -void LLDrawPoolBump::endShiny(bool invisible) +//static +void LLDrawPoolBump::unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible) { - LLFastTimer t(FTM_RENDER_SHINY); - if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| - (invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY))) - { - return; - } - LLCubeMap* cube_map = gSky.mVOSkyp ? gSky.mVOSkyp->getCubeMap() : NULL; if( cube_map ) { cube_map->disable(); cube_map->restoreMatrix(); - if (!invisible && mVertexShaderLevel > 1) + if (!invisible && shader_level > 1) { shader->disableTexture(LLViewerShaderMgr::ENVIRONMENT_MAP, LLTexUnit::TT_CUBE_MAP); @@ -434,7 +441,6 @@ void LLDrawPoolBump::endShiny(bool invisible) shader->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); } } - shader->unbind(); } } gGL.getTexUnit(diffuse_channel)->disable(); @@ -442,6 +448,22 @@ void LLDrawPoolBump::endShiny(bool invisible) gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.getTexUnit(0)->setTextureBlendType(LLTexUnit::TB_MULT); +} + +void LLDrawPoolBump::endShiny(bool invisible) +{ + LLFastTimer t(FTM_RENDER_SHINY); + if ((!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY))|| + (invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY))) + { + return; + } + + unbindCubeMap(shader, mVertexShaderLevel, diffuse_channel, cube_channel, invisible); + if (shader) + { + shader->unbind(); + } diffuse_channel = -1; cube_channel = 0; diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h index 2019f1df26..89bbefe778 100644 --- a/indra/newview/lldrawpoolbump.h +++ b/indra/newview/lldrawpoolbump.h @@ -41,6 +41,7 @@ class LLImageRaw; class LLSpatialGroup; class LLDrawInfo; +class LLGLSLShader; class LLViewerFetchedTexture; class LLDrawPoolBump : public LLRenderPass @@ -79,6 +80,9 @@ public: void renderBump(); void endBump(); + static void bindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible); + static void unbindCubeMap(LLGLSLShader* shader, S32 shader_level, S32& diffuse_channel, S32& cube_channel, bool invisible); + virtual S32 getNumDeferredPasses(); /*virtual*/ void beginDeferredPass(S32 pass); /*virtual*/ void endDeferredPass(S32 pass); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 7866e49bae..0e0b8447ca 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -205,7 +205,12 @@ void LLFace::destroy() if (mDrawPoolp) { LLFastTimer t(FTM_DESTROY_DRAWPOOL); + + if (this->isState(LLFace::RIGGED) && mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR) mDrawPoolp->removeFace(this); + + + mDrawPoolp = NULL; } diff --git a/indra/newview/llface.h b/indra/newview/llface.h index bbf8de04bc..2b8fdf2e58 100644 --- a/indra/newview/llface.h +++ b/indra/newview/llface.h @@ -234,6 +234,7 @@ public: private: friend class LLGeometryManager; friend class LLVolumeGeometryManager; + friend class LLDrawPoolAvatar; U32 mState; LLFacePool* mDrawPoolp; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 6bd3ceb8a8..3aecd0175d 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -5219,6 +5219,25 @@ void LLViewerObject::resetChildrenPosition(const LLVector3& offset, BOOL simplif return ; } +//virtual +LLVOAvatar* LLViewerObject::getAvatar() const +{ + if (isAttachment()) + { + LLViewerObject* vobj = (LLViewerObject*) getParent(); + + while (vobj && !vobj->asAvatar()) + { + vobj = (LLViewerObject*) vobj->getParent(); + } + + return (LLVOAvatar*) vobj; + } + + return NULL; +} + + class ObjectPhysicsProperties : public LLHTTPNode { public: diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h index 594d7a0827..0fd0cbfa60 100644 --- a/indra/newview/llviewerobject.h +++ b/indra/newview/llviewerobject.h @@ -181,6 +181,7 @@ public: void setOnActiveList(BOOL on_active) { mOnActiveList = on_active; } virtual BOOL isAttachment() const { return FALSE; } + virtual LLVOAvatar* getAvatar() const; //get the avatar this object is attached to, or NULL if object is not an attachment virtual BOOL isHUDAttachment() const { return FALSE; } virtual void updateRadius() {}; virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius() diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index d978e856a6..fe68d6eaa4 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -79,6 +79,7 @@ LLGLSLShader gObjectShinyWaterProgram; //object hardware skinning shaders LLGLSLShader gSkinnedObjectSimpleProgram; +LLGLSLShader gSkinnedObjectShinySimpleProgram; //environment shaders LLGLSLShader gTerrainProgram; @@ -154,6 +155,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() : mShaderList.push_back(&gObjectFullbrightProgram); mShaderList.push_back(&gObjectFullbrightShinyProgram); mShaderList.push_back(&gSkinnedObjectSimpleProgram); + mShaderList.push_back(&gSkinnedObjectShinySimpleProgram); mShaderList.push_back(&gTerrainProgram); mShaderList.push_back(&gTerrainWaterProgram); mShaderList.push_back(&gObjectSimpleWaterProgram); @@ -505,6 +507,9 @@ void LLViewerShaderMgr::setShaders() if (!loadShadersDeferred()) { gSavedSettings.setBOOL("RenderDeferred", FALSE); + reentrance = false; + setShaders(); + return; } #endif } @@ -557,6 +562,8 @@ void LLViewerShaderMgr::unloadShaders() gObjectShinyWaterProgram.unload(); gSkinnedObjectSimpleProgram.unload(); + gSkinnedObjectShinySimpleProgram.unload(); + gWaterProgram.unload(); gUnderWaterProgram.unload(); @@ -917,7 +924,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredGIProgram.unload(); gDeferredGIFinalProgram.unload(); gDeferredWaterProgram.unload(); - return FALSE; + return TRUE; } mVertexShaderLevel[SHADER_AVATAR] = 1; @@ -1251,6 +1258,8 @@ BOOL LLViewerShaderMgr::loadShadersObject() gObjectFullbrightProgram.unload(); gObjectFullbrightWaterProgram.unload(); gSkinnedObjectSimpleProgram.unload(); + gSkinnedObjectShinySimpleProgram.unload(); + return FALSE; } @@ -1376,6 +1385,22 @@ BOOL LLViewerShaderMgr::loadShadersObject() success = gSkinnedObjectSimpleProgram.createShader(NULL, NULL); } + if (success) + { + gSkinnedObjectShinySimpleProgram.mName = "Skinned Shiny Simple Shader"; + gSkinnedObjectShinySimpleProgram.mFeatures.calculatesLighting = true; + gSkinnedObjectShinySimpleProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectShinySimpleProgram.mFeatures.hasGamma = true; + gSkinnedObjectShinySimpleProgram.mFeatures.hasAtmospherics = true; + gSkinnedObjectShinySimpleProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectShinySimpleProgram.mFeatures.isShiny = true; + gSkinnedObjectShinySimpleProgram.mShaderFiles.clear(); + gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinySimpleSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectShinySimpleProgram.mShaderFiles.push_back(make_pair("objects/shinyF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectShinySimpleProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectShinySimpleProgram.createShader(NULL, &mShinyUniforms); + } + if( !success ) { mVertexShaderLevel[SHADER_OBJECT] = 0; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index b279a59777..beac5462e2 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -315,6 +315,7 @@ extern LLGLSLShader gObjectShinyProgram; extern LLGLSLShader gObjectShinyWaterProgram; extern LLGLSLShader gSkinnedObjectSimpleProgram; +extern LLGLSLShader gSkinnedObjectShinySimpleProgram; //environment shaders extern LLGLSLShader gTerrainProgram; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 7cdbebf4d1..c51a7d9cbb 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -3642,7 +3642,7 @@ bool LLVOAvatar::shouldAlphaMask() U32 LLVOAvatar::renderSkinnedAttachments() { - U32 num_indices = 0; + /*U32 num_indices = 0; const U32 data_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | @@ -3670,107 +3670,14 @@ U32 LLVOAvatar::renderSkinnedAttachments() LLFace* face = drawable->getFace(i); if (face->isState(LLFace::RIGGED)) { - LLVolume* volume = attached_object->getVolume(); - if (!volume || volume->getNumVolumeFaces() <= i) - { - continue; - } - - const LLVolumeFace& vol_face = volume->getVolumeFace(i); - - const LLMeshSkinInfo* skin = NULL; - LLVertexBuffer* buff = face->mVertexBuffer; - LLUUID mesh_id = volume->getParams().getSculptID();; - - if (!buff || - !buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) || - buff->getRequestedVerts() != vol_face.mVertices.size()) - { - face->mVertexBuffer = NULL; - face->mLastVertexBuffer = NULL; - buff = NULL; - - if (mesh_id.notNull()) - { - skin = gMeshRepo.getSkinInfo(mesh_id); - if (skin) - { - face->mVertexBuffer = new LLVertexBuffer(data_mask, 0); - face->mVertexBuffer->allocateBuffer(vol_face.mVertices.size(), vol_face.mIndices.size(), true); - - face->setGeomIndex(0); - face->setIndicesIndex(0); - face->setSize(vol_face.mVertices.size(), vol_face.mIndices.size()); - - U16 offset = 0; - - LLMatrix4 mat_vert = skin->mBindShapeMatrix; - glh::matrix4f m((F32*) mat_vert.mMatrix); - m = m.inverse().transpose(); - - F32 mat3[] = - { m.m[0], m.m[1], m.m[2], - m.m[4], m.m[5], m.m[6], - m.m[8], m.m[9], m.m[10] }; - - LLMatrix3 mat_normal(mat3); - - face->getGeometryVolume(*volume, i, mat_vert, mat_normal, offset, true); - buff = face->mVertexBuffer; - } - } - } - if (buff && mesh_id.notNull()) - { - if (!skin) - { - skin = gMeshRepo.getSkinInfo(mesh_id); - } - - if (skin) - { - LLMatrix4 mat[64]; - - for (U32 i = 0; i < skin->mJointNames.size(); ++i) - { - LLJoint* joint = getJoint(skin->mJointNames[i]); - if (joint) - { - mat[i] = skin->mInvBindMatrix[i]; - mat[i] *= joint->getWorldMatrix(); - } - } - - LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv("matrixPalette", - skin->mJointNames.size(), - FALSE, - (GLfloat*) mat[0].mMatrix); - LLDrawPoolAvatar::sVertexProgram->uniformMatrix4fv("matrixPalette[0]", - skin->mJointNames.size(), - FALSE, - (GLfloat*) mat[0].mMatrix); - - buff->setBuffer(data_mask); - - U16 start = face->getGeomStart(); - U16 end = start + face->getGeomCount()-1; - S32 offset = face->getIndicesStart(); - U32 count = face->getIndicesCount(); - - gGL.getTexUnit(0)->bind(face->getTexture()); - buff->drawRange(LLRender::TRIANGLES, start, end, count, offset); - - } - } - } - } } } } } - return num_indices; + return num_indices;*/ + return 0; } //----------------------------------------------------------------------------- diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 96f69b3676..e49b33bd80 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -52,6 +52,7 @@ #include "object_flags.h" #include "llagentconstants.h" #include "lldrawable.h" +#include "lldrawpoolavatar.h" #include "lldrawpoolbump.h" #include "llface.h" #include "llspatialpartition.h" @@ -73,6 +74,8 @@ #include "llmeshrepository.h" #include "llagent.h" #include "llviewermediafocus.h" +#include "llvoavatar.h" + const S32 MIN_QUIET_FRAMES_COALESCE = 30; const F32 FORCE_SIMPLE_RENDER_AREA = 512.f; @@ -3415,6 +3418,33 @@ void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group) static LLFastTimer::DeclareTimer FTM_REBUILD_VOLUME_VB("Volume"); static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt"); +LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj) +{ + LLVOAvatar* avatar = vobj->getAvatar(); + + if (avatar) + { + LLDrawable* drawable = avatar->mDrawable; + if (drawable && drawable->getNumFaces() > 0) + { + LLFace* face = drawable->getFace(0); + if (face) + { + LLDrawPool* drawpool = face->getPool(); + if (drawpool) + { + if (drawpool->getType() == LLDrawPool::POOL_AVATAR) + { + return (LLDrawPoolAvatar*) drawpool; + } + } + } + } + } + + return NULL; +} + void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { @@ -3499,13 +3529,47 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) facep->mVertexBuffer = NULL; facep->mLastVertexBuffer = NULL; facep->setState(LLFace::RIGGED); + + //get drawpool of avatar with rigged face + LLDrawPoolAvatar* pool = get_avatar_drawpool(vobj); + + if (pool) + { + const LLTextureEntry* te = facep->getTextureEntry(); + + //remove face from old pool if it exists + LLDrawPool* old_pool = facep->getPool(); + if (old_pool && old_pool->getType() == LLDrawPool::POOL_AVATAR) + { + ((LLDrawPoolAvatar*) old_pool)->removeRiggedFace(facep); + } + + //add face to new pool + if (te->getShiny()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SHINY_SIMPLE); + } + else + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE); + } + } + } continue; } else { - facep->clearState(LLFace::RIGGED); + if (facep->isState(LLFace::RIGGED)) + { //face is not rigged but used to be, remove from rigged face pool + LLDrawPoolAvatar* pool = (LLDrawPoolAvatar*) facep->getPool(); + if (pool) + { + pool->removeRiggedFace(facep); + } + facep->clearState(LLFace::RIGGED); + } } if (cur_total > max_total || facep->getIndicesCount() <= 0 || facep->getGeomCount() <= 0) -- cgit v1.3 From 2f95a549a365ca2bedf7824014a687b3af88e20f Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Sat, 1 May 2010 01:55:21 -0500 Subject: Fullbrigt skinned and fix for silly crash from not removing face references. --- .../shaders/class1/objects/shinyV.glsl | 2 +- indra/newview/lldrawpoolavatar.cpp | 40 +++++++++++++++++++++- indra/newview/lldrawpoolavatar.h | 5 ++- indra/newview/llface.cpp | 13 ++++--- indra/newview/llviewershadermgr.cpp | 19 ++++++++++ indra/newview/llviewershadermgr.h | 1 + indra/newview/llvovolume.cpp | 9 ++++- 7 files changed, 81 insertions(+), 8 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl index c2e1ddf734..101458c438 100644 --- a/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl +++ b/indra/newview/app_settings/shaders/class1/objects/shinyV.glsl @@ -12,7 +12,7 @@ uniform vec4 origin; void main() { //transform vertex - gl_Position = ftransform(); //gl_ModelViewProjectionMatrix * gl_Vertex; + gl_Position = ftransform(); vec4 pos = (gl_ModelViewMatrix * gl_Vertex); vec3 norm = normalize(gl_NormalMatrix * gl_Normal); diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 02c7e3bb6f..9463be6059 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -364,7 +364,7 @@ S32 LLDrawPoolAvatar::getNumPasses() } else if (getVertexShaderLevel() > 0) { - return 5; + return 6; } else { @@ -411,6 +411,9 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass) beginRiggedSimple(); break; case 4: + beginRiggedFullbright(); + break; + case 5: beginRiggedShinySimple(); break; } @@ -441,6 +444,9 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass) endRiggedSimple(); break; case 4: + endRiggedFullbright(); + break; + case 5: endRiggedShinySimple(); break; } @@ -644,6 +650,22 @@ void LLDrawPoolAvatar::endRiggedSimple() LLVertexBuffer::sWeight4Loc = -1; } +void LLDrawPoolAvatar::beginRiggedFullbright() +{ + sVertexProgram = &gSkinnedObjectFullbrightProgram; + diffuse_channel = 0; + gSkinnedObjectFullbrightProgram.bind(); + LLVertexBuffer::sWeight4Loc = gSkinnedObjectFullbrightProgram.getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); +} + +void LLDrawPoolAvatar::endRiggedFullbright() +{ + sVertexProgram = NULL; + LLVertexBuffer::unbind(); + gSkinnedObjectFullbrightProgram.unbind(); + LLVertexBuffer::sWeight4Loc = -1; +} + void LLDrawPoolAvatar::beginRiggedShinySimple() { sVertexProgram = &gSkinnedObjectShinySimpleProgram; @@ -825,6 +847,12 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) } if (pass == 4) + { + renderRiggedFullbright(avatarp); + return; + } + + if (pass == 5) { renderRiggedShinySimple(avatarp); return; @@ -990,6 +1018,16 @@ void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar) renderRigged(avatar, RIGGED_SIMPLE, data_mask); } +void LLDrawPoolAvatar::renderRiggedFullbright(LLVOAvatar* avatar) +{ + const U32 data_mask = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4; + + renderRigged(avatar, RIGGED_FULLBRIGHT, data_mask); +} + void LLDrawPoolAvatar::renderRiggedShinySimple(LLVOAvatar* avatar) { diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index 0ebb035f2a..8443069376 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -97,12 +97,14 @@ public: void beginImpostor(); void beginSkinned(); void beginRiggedSimple(); + void beginRiggedFullbright(); void beginRiggedShinySimple(); void endRigid(); void endImpostor(); void endSkinned(); void endRiggedSimple(); + void endRiggedFullbright(); void endRiggedShinySimple(); void beginDeferredImpostor(); @@ -123,6 +125,7 @@ public: void renderRigged(LLVOAvatar* avatar, U32 type, const U32 data_mask); void renderRiggedSimple(LLVOAvatar* avatar); + void renderRiggedFullbright(LLVOAvatar* avatar); void renderRiggedShinySimple(LLVOAvatar* avatar); /*virtual*/ LLViewerTexture *getDebugTexture(); @@ -133,11 +136,11 @@ public: typedef enum { RIGGED_SIMPLE = 0, + RIGGED_FULLBRIGHT, RIGGED_SHINY_SIMPLE, RIGGED_SHINY_FULLBRIGHT, RIGGED_SHINY_BUMP, RIGGED_BUMP, - RIGGED_FULLBRIGHT, RIGGED_ALPHA, NUM_RIGGED_PASSES, RIGGED_UNKNOWN, diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 0e0b8447ca..9df692e787 100644 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -41,6 +41,7 @@ #include "m3math.h" #include "v3color.h" +#include "lldrawpoolavatar.h" #include "lldrawpoolbump.h" #include "llgl.h" #include "llrender.h" @@ -207,10 +208,14 @@ void LLFace::destroy() LLFastTimer t(FTM_DESTROY_DRAWPOOL); if (this->isState(LLFace::RIGGED) && mDrawPoolp->getType() == LLDrawPool::POOL_AVATAR) - mDrawPoolp->removeFace(this); - - - + { + ((LLDrawPoolAvatar*) mDrawPoolp)->removeRiggedFace(this); + } + else + { + mDrawPoolp->removeFace(this); + } + mDrawPoolp = NULL; } diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index fe68d6eaa4..3cc5b4357a 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -79,6 +79,7 @@ LLGLSLShader gObjectShinyWaterProgram; //object hardware skinning shaders LLGLSLShader gSkinnedObjectSimpleProgram; +LLGLSLShader gSkinnedObjectFullbrightProgram; LLGLSLShader gSkinnedObjectShinySimpleProgram; //environment shaders @@ -155,6 +156,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() : mShaderList.push_back(&gObjectFullbrightProgram); mShaderList.push_back(&gObjectFullbrightShinyProgram); mShaderList.push_back(&gSkinnedObjectSimpleProgram); + mShaderList.push_back(&gSkinnedObjectFullbrightProgram); mShaderList.push_back(&gSkinnedObjectShinySimpleProgram); mShaderList.push_back(&gTerrainProgram); mShaderList.push_back(&gTerrainWaterProgram); @@ -562,6 +564,7 @@ void LLViewerShaderMgr::unloadShaders() gObjectShinyWaterProgram.unload(); gSkinnedObjectSimpleProgram.unload(); + gSkinnedObjectFullbrightProgram.unload(); gSkinnedObjectShinySimpleProgram.unload(); @@ -1258,6 +1261,7 @@ BOOL LLViewerShaderMgr::loadShadersObject() gObjectFullbrightProgram.unload(); gObjectFullbrightWaterProgram.unload(); gSkinnedObjectSimpleProgram.unload(); + gSkinnedObjectFullbrightProgram.unload(); gSkinnedObjectShinySimpleProgram.unload(); return FALSE; @@ -1385,6 +1389,21 @@ BOOL LLViewerShaderMgr::loadShadersObject() success = gSkinnedObjectSimpleProgram.createShader(NULL, NULL); } + if (success) + { + gSkinnedObjectFullbrightProgram.mName = "Skinned Fullbright Shader"; + gSkinnedObjectFullbrightProgram.mFeatures.calculatesAtmospherics = true; + gSkinnedObjectFullbrightProgram.mFeatures.hasGamma = true; + gSkinnedObjectFullbrightProgram.mFeatures.hasTransport = true; + gSkinnedObjectFullbrightProgram.mFeatures.isFullbright = true; + gSkinnedObjectFullbrightProgram.mFeatures.hasObjectSkinning = true; + gSkinnedObjectFullbrightProgram.mShaderFiles.clear(); + gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gSkinnedObjectFullbrightProgram.mShaderFiles.push_back(make_pair("objects/fullbrightF.glsl", GL_FRAGMENT_SHADER_ARB)); + gSkinnedObjectFullbrightProgram.mShaderLevel = mVertexShaderLevel[SHADER_OBJECT]; + success = gSkinnedObjectFullbrightProgram.createShader(NULL, NULL); + } + if (success) { gSkinnedObjectShinySimpleProgram.mName = "Skinned Shiny Simple Shader"; diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index beac5462e2..b4fc336bf3 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -315,6 +315,7 @@ extern LLGLSLShader gObjectShinyProgram; extern LLGLSLShader gObjectShinyWaterProgram; extern LLGLSLShader gSkinnedObjectSimpleProgram; +extern LLGLSLShader gSkinnedObjectFullbrightProgram; extern LLGLSLShader gSkinnedObjectShinySimpleProgram; //environment shaders diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index e49b33bd80..3449c05be8 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -3551,7 +3551,14 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } else { - pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE); + if (te->getFullbright()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_FULLBRIGHT); + } + else + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_SIMPLE); + } } } -- cgit v1.3 From eb283701afc7ecbe3009a9fb75be1dcb222a383b Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 4 May 2010 00:45:28 -0500 Subject: Deferred pipeline integration of rigged attachments and cleanup. --- indra/llrender/llvertexbuffer.h | 2 +- .../shaders/class1/deferred/alphaF.glsl | 5 +- .../shaders/class1/deferred/alphaV.glsl | 2 +- .../shaders/class1/deferred/avatarAlphaV.glsl | 4 +- .../shaders/class1/deferred/diffuseV.glsl | 2 +- .../shaders/class2/deferred/alphaF.glsl | 2 +- indra/newview/lldrawpoolavatar.cpp | 302 +++++++++++++++------ indra/newview/lldrawpoolavatar.h | 51 +++- indra/newview/lldrawpoolbump.cpp | 27 +- indra/newview/lldrawpoolbump.h | 7 +- indra/newview/llviewershadermgr.cpp | 36 ++- indra/newview/llviewershadermgr.h | 2 + indra/newview/llvovolume.cpp | 15 + 13 files changed, 359 insertions(+), 98 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/llrender/llvertexbuffer.h b/indra/llrender/llvertexbuffer.h index 225237215c..d1700aa54a 100644 --- a/indra/llrender/llvertexbuffer.h +++ b/indra/llrender/llvertexbuffer.h @@ -190,7 +190,7 @@ public: U8* getIndicesPointer() const { return useVBOs() ? NULL : mMappedIndexData; } U8* getVerticesPointer() const { return useVBOs() ? NULL : mMappedData; } S32 getStride() const { return mStride; } - S32 getTypeMask() const { return mTypeMask; } + U32 getTypeMask() const { return mTypeMask; } BOOL hasDataType(S32 type) const { return ((1 << type) & getTypeMask()) ? TRUE : FALSE; } S32 getSize() const { return mNumVerts*mStride; } S32 getIndicesSize() const { return mNumIndices * sizeof(U16); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl index fea2e16090..6f027de6a2 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl @@ -22,7 +22,6 @@ varying vec3 vary_ambient; varying vec3 vary_directional; varying vec3 vary_fragcoord; varying vec3 vary_position; -varying vec3 vary_light; uniform mat4 inv_proj; @@ -55,8 +54,8 @@ void main() color.rgb = scaleSoftClip(color.rgb); - //gl_FragColor = gl_Color; - gl_FragColor = color; + gl_FragColor = gl_Color; + //gl_FragColor = color; //gl_FragColor = vec4(1,0,1,1); //gl_FragColor = vec4(1,0,1,1)*shadow; diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl index 04e556c11a..43200f1b07 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaV.glsl @@ -36,7 +36,7 @@ void main() vec4 pos = (gl_ModelViewMatrix * gl_Vertex); vec3 norm = normalize(gl_NormalMatrix * gl_Normal); - vary_position = pos.xyz + norm.xyz * (-pos.z/64.0*shadow_offset+shadow_bias); + vary_position = pos.xyz; calcAtmospherics(pos.xyz); diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl index 650fbcc3f5..da1dafda36 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarAlphaV.glsl @@ -17,7 +17,7 @@ vec3 atmosAffectDirectionalLight(float lightIntensity); vec3 scaleDownLight(vec3 light); vec3 scaleUpLight(vec3 light); -varying vec4 vary_position; +varying vec3 vary_position; varying vec3 vary_ambient; varying vec3 vary_directional; varying vec3 vary_normal; @@ -41,7 +41,7 @@ void main() norm = normalize(norm); gl_Position = gl_ProjectionMatrix * pos; - vary_position = pos; + vary_position = pos.xyz; vary_normal = norm; calcAtmospherics(pos.xyz); diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl index b458842657..44468cdfa2 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseV.glsl @@ -13,7 +13,7 @@ void main() gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; - vary_nomral = normalize(gl_NormalMatrix * gl_Normal); + vary_normal = normalize(gl_NormalMatrix * gl_Normal); gl_FrontColor = gl_Color; } diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl index 665fe16b43..fa6b4e2afb 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaF.glsl @@ -115,7 +115,7 @@ void main() //gl_FragColor = gl_Color; gl_FragColor = color; - //gl_FragColor = vec4(1,0,1,1)*shadow; + //gl_FragColor = vec4(1,shadow,1,1); } diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index 866aea42c4..ac599caa5b 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -63,6 +63,8 @@ LLGLSLShader* LLDrawPoolAvatar::sVertexProgram = NULL; BOOL LLDrawPoolAvatar::sSkipOpaque = FALSE; BOOL LLDrawPoolAvatar::sSkipTransparent = FALSE; +static bool is_deferred_render = false; + extern BOOL gUseGLPick; F32 CLOTHING_GRAVITY_EFFECT = 0.7f; @@ -100,6 +102,18 @@ S32 specular_channel = -1; S32 diffuse_channel = -1; S32 cube_channel = -1; +static const U32 rigged_data_mask[] = { + LLDrawPoolAvatar::RIGGED_SIMPLE_MASK, + LLDrawPoolAvatar::RIGGED_FULLBRIGHT_MASK, + LLDrawPoolAvatar::RIGGED_SHINY_MASK, + LLDrawPoolAvatar::RIGGED_FULLBRIGHT_SHINY_MASK, + LLDrawPoolAvatar::RIGGED_GLOW_MASK, + LLDrawPoolAvatar::RIGGED_ALPHA_MASK, + LLDrawPoolAvatar::RIGGED_FULLBRIGHT_ALPHA_MASK, + LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP_MASK, + LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE_MASK, +}; + static LLFastTimer::DeclareTimer FTM_SHADOW_AVATAR("Avatar Shadow"); @@ -155,21 +169,17 @@ LLMatrix4& LLDrawPoolAvatar::getModelView() //----------------------------------------------------------------------------- -S32 LLDrawPoolAvatar::getNumDeferredPasses() -{ - return getNumPasses(); -} void LLDrawPoolAvatar::beginDeferredPass(S32 pass) { LLFastTimer t(FTM_RENDER_CHARACTERS); sSkipTransparent = TRUE; - + is_deferred_render = true; + if (LLPipeline::sImpostorRender) - { - beginDeferredSkinned(); - return; + { //impostor pass does not have rigid or impostor rendering + pass += 2; } switch (pass) @@ -184,7 +194,10 @@ void LLDrawPoolAvatar::beginDeferredPass(S32 pass) beginDeferredSkinned(); break; case 3: - beginDeferredRigged(); + beginDeferredRiggedSimple(); + break; + case 4: + beginDeferredRiggedBump(); break; } } @@ -194,11 +207,11 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass) LLFastTimer t(FTM_RENDER_CHARACTERS); sSkipTransparent = FALSE; + is_deferred_render = false; if (LLPipeline::sImpostorRender) { - endDeferredSkinned(); - return; + pass += 2; } switch (pass) @@ -213,7 +226,11 @@ void LLDrawPoolAvatar::endDeferredPass(S32 pass) endDeferredSkinned(); break; case 3: - endDeferredRigged(); + endDeferredRiggedSimple(); + break; + case 4: + endDeferredRiggedBump(); + break; } } @@ -224,10 +241,35 @@ void LLDrawPoolAvatar::renderDeferred(S32 pass) S32 LLDrawPoolAvatar::getNumPostDeferredPasses() { - return 1; + return 6; } void LLDrawPoolAvatar::beginPostDeferredPass(S32 pass) +{ + switch (pass) + { + case 0: + beginPostDeferredAlpha(); + break; + case 1: + beginRiggedFullbright(); + break; + case 2: + beginRiggedFullbrightShiny(); + break; + case 3: + beginDeferredRiggedAlpha(); + break; + case 4: + beginRiggedFullbrightAlpha(); + break; + case 5: + beginRiggedGlow(); + break; + } +} + +void LLDrawPoolAvatar::beginPostDeferredAlpha() { sSkipOpaque = TRUE; sShaderLevel = mVertexShaderLevel; @@ -240,7 +282,49 @@ void LLDrawPoolAvatar::beginPostDeferredPass(S32 pass) enable_vertex_weighting(sVertexProgram->mAttribute[LLViewerShaderMgr::AVATAR_WEIGHT]); } +void LLDrawPoolAvatar::beginDeferredRiggedAlpha() +{ + sVertexProgram = &gDeferredSkinnedAlphaProgram; + gPipeline.bindDeferredShader(*sVertexProgram); + diffuse_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); + gPipeline.enableLightsDynamic(); +} + +void LLDrawPoolAvatar::endDeferredRiggedAlpha() +{ + LLVertexBuffer::unbind(); + gPipeline.unbindDeferredShader(*sVertexProgram); + LLVertexBuffer::sWeight4Loc = -1; + sVertexProgram = NULL; +} + void LLDrawPoolAvatar::endPostDeferredPass(S32 pass) +{ + switch (pass) + { + case 0: + endPostDeferredAlpha(); + break; + case 1: + endRiggedFullbright(); + break; + case 2: + endRiggedFullbrightShiny(); + break; + case 3: + endDeferredRiggedAlpha(); + break; + case 4: + endRiggedFullbrightAlpha(); + break; + case 5: + endRiggedGlow(); + break; + } +} + +void LLDrawPoolAvatar::endPostDeferredAlpha() { // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done sRenderingSkinned = FALSE; @@ -254,7 +338,17 @@ void LLDrawPoolAvatar::endPostDeferredPass(S32 pass) void LLDrawPoolAvatar::renderPostDeferred(S32 pass) { - render(2); //pass 2 = skinned + const S32 actual_pass[] = + { //map post deferred pass numbers to what render() expects + 2, //skinned + 4, // rigged fullbright + 6, //rigged fullbright shiny + 7, //rigged alpha + 8, //rigged fullbright alpha + 9, //rigged glow + }; + + render(actual_pass[pass]); } @@ -288,6 +382,7 @@ void LLDrawPoolAvatar::beginShadowPass(S32 pass) else { sVertexProgram = &gDeferredAttachmentShadowProgram; + diffuse_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); sVertexProgram->bind(); LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); } @@ -352,7 +447,12 @@ void LLDrawPoolAvatar::renderShadow(S32 pass) } else { - avatarp->renderSkinnedAttachments(); + renderRigged(avatarp, RIGGED_SIMPLE); + renderRigged(avatarp, RIGGED_ALPHA); + renderRigged(avatarp, RIGGED_FULLBRIGHT); + renderRigged(avatarp, RIGGED_FULLBRIGHT_SHINY); + renderRigged(avatarp, RIGGED_SHINY); + renderRigged(avatarp, RIGGED_FULLBRIGHT_ALPHA); } } @@ -360,7 +460,7 @@ S32 LLDrawPoolAvatar::getNumPasses() { if (LLPipeline::sImpostorRender) { - return 1; + return 8; } else if (getVertexShaderLevel() > 0) { @@ -372,6 +472,19 @@ S32 LLDrawPoolAvatar::getNumPasses() } } +S32 LLDrawPoolAvatar::getNumDeferredPasses() +{ + if (LLPipeline::sImpostorRender) + { + return 3; + } + else + { + return 5; + } +} + + void LLDrawPoolAvatar::render(S32 pass) { LLFastTimer t(FTM_RENDER_CHARACTERS); @@ -391,9 +504,8 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass) LLVertexBuffer::unbind(); if (LLPipeline::sImpostorRender) - { - beginSkinned(); - return; + { //impostor render does not have impostors or rigid rendering + pass += 2; } switch (pass) @@ -437,8 +549,7 @@ void LLDrawPoolAvatar::endRenderPass(S32 pass) if (LLPipeline::sImpostorRender) { - endSkinned(); - return; + pass += 2; } switch (pass) @@ -536,8 +647,8 @@ void LLDrawPoolAvatar::beginDeferredImpostor() sVertexProgram = &gDeferredImpostorProgram; - normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL); specular_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::SPECULAR_MAP); + normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DEFERRED_NORMAL); diffuse_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); sVertexProgram->bind(); @@ -774,18 +885,40 @@ void LLDrawPoolAvatar::endRiggedFullbrightShiny() } -void LLDrawPoolAvatar::beginDeferredRigged() +void LLDrawPoolAvatar::beginDeferredRiggedSimple() { sVertexProgram = &gDeferredSkinnedDiffuseProgram; + diffuse_channel = 0; + sVertexProgram->bind(); + LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); +} + +void LLDrawPoolAvatar::endDeferredRiggedSimple() +{ + LLVertexBuffer::unbind(); + sVertexProgram->unbind(); + LLVertexBuffer::sWeight4Loc = -1; + sVertexProgram = NULL; +} + +void LLDrawPoolAvatar::beginDeferredRiggedBump() +{ + sVertexProgram = &gDeferredSkinnedBumpProgram; sVertexProgram->bind(); + normal_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::BUMP_MAP); + diffuse_channel = sVertexProgram->enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); LLVertexBuffer::sWeight4Loc = sVertexProgram->getAttribLocation(LLViewerShaderMgr::OBJECT_WEIGHT); } -void LLDrawPoolAvatar::endDeferredRigged() +void LLDrawPoolAvatar::endDeferredRiggedBump() { LLVertexBuffer::unbind(); + sVertexProgram->disableTexture(LLViewerShaderMgr::BUMP_MAP); + sVertexProgram->disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); sVertexProgram->unbind(); LLVertexBuffer::sWeight4Loc = -1; + normal_channel = -1; + diffuse_channel = -1; sVertexProgram = NULL; } @@ -933,13 +1066,28 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) if (pass == 3) { - renderRiggedSimple(avatarp); + if (is_deferred_render) + { + renderDeferredRiggedSimple(avatarp); + } + else + { + renderRiggedSimple(avatarp); + } return; } if (pass == 4) { - renderRiggedFullbright(avatarp); + if (is_deferred_render) + { + renderDeferredRiggedBump(avatarp); + } + else + { + renderRiggedFullbright(avatarp); + } + return; } @@ -995,6 +1143,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) renderRiggedGlow(avatarp); gGL.setColorMask(true, false); gGL.setSceneBlendType(LLRender::BT_ALPHA); + return; } @@ -1034,12 +1183,21 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) } } -void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face, U32 data_mask) +void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLFace* face, const LLMeshSkinInfo* skin, LLVolume* volume, const LLVolumeFace& vol_face) { + U32 data_mask = 0; + for (U32 i = 0; i < face->mRiggedIndex.size(); ++i) + { + if (face->mRiggedIndex[i] > -1) + { + data_mask |= rigged_data_mask[i]; + } + } + LLVertexBuffer* buff = face->mVertexBuffer; if (!buff || - !buff->hasDataType(LLVertexBuffer::TYPE_WEIGHT4) || + buff->getTypeMask() != data_mask || buff->getRequestedVerts() != vol_face.mVertices.size()) { face->setGeomIndex(0); @@ -1067,7 +1225,7 @@ void LLDrawPoolAvatar::updateRiggedFaceVertexBuffer(LLFace* face, const LLMeshSk } } -void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, const U32 data_mask, bool glow) +void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, bool glow) { for (U32 i = 0; i < mRiggedFace[type].size(); ++i) { @@ -1106,8 +1264,10 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, const U32 data } const LLVolumeFace& vol_face = volume->getVolumeFace(te); - updateRiggedFaceVertexBuffer(face, skin, volume, vol_face, data_mask); + updateRiggedFaceVertexBuffer(face, skin, volume, vol_face); + U32 data_mask = rigged_data_mask[type]; + LLVertexBuffer* buff = face->mVertexBuffer; if (buff) @@ -1145,84 +1305,72 @@ void LLDrawPoolAvatar::renderRigged(LLVOAvatar* avatar, U32 type, const U32 data glColor4f(0,0,0,face->getTextureEntry()->getGlow()); } - gGL.getTexUnit(0)->bind(face->getTexture()); - buff->drawRange(LLRender::TRIANGLES, start, end, count, offset); + gGL.getTexUnit(diffuse_channel)->bind(face->getTexture()); + if (normal_channel > -1) + { + LLDrawPoolBump::bindBumpMap(face, normal_channel); + } + + if (face->mTextureMatrix) + { + glMatrixMode(GL_TEXTURE); + glLoadMatrixf((F32*) face->mTextureMatrix->mMatrix); + buff->drawRange(LLRender::TRIANGLES, start, end, count, offset); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + } + else + { + buff->drawRange(LLRender::TRIANGLES, start, end, count, offset); + } } } } -void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar) +void LLDrawPoolAvatar::renderDeferredRiggedSimple(LLVOAvatar* avatar) { - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; + renderRigged(avatar, RIGGED_DEFERRED_SIMPLE); +} - renderRigged(avatar, RIGGED_SIMPLE, data_mask); +void LLDrawPoolAvatar::renderDeferredRiggedBump(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_DEFERRED_BUMP); } -void LLDrawPoolAvatar::renderRiggedFullbright(LLVOAvatar* avatar) +void LLDrawPoolAvatar::renderRiggedSimple(LLVOAvatar* avatar) { - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; + renderRigged(avatar, RIGGED_SIMPLE); +} - renderRigged(avatar, RIGGED_FULLBRIGHT, data_mask); +void LLDrawPoolAvatar::renderRiggedFullbright(LLVOAvatar* avatar) +{ + renderRigged(avatar, RIGGED_FULLBRIGHT); } void LLDrawPoolAvatar::renderRiggedShinySimple(LLVOAvatar* avatar) { - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; - - renderRigged(avatar, RIGGED_SHINY, data_mask); + renderRigged(avatar, RIGGED_SHINY); } void LLDrawPoolAvatar::renderRiggedFullbrightShiny(LLVOAvatar* avatar) { - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; - - renderRigged(avatar, RIGGED_FULLBRIGHT_SHINY, data_mask); + renderRigged(avatar, RIGGED_FULLBRIGHT_SHINY); } void LLDrawPoolAvatar::renderRiggedAlpha(LLVOAvatar* avatar) { - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_NORMAL | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; - - renderRigged(avatar, RIGGED_ALPHA, data_mask); + renderRigged(avatar, RIGGED_ALPHA); } void LLDrawPoolAvatar::renderRiggedFullbrightAlpha(LLVOAvatar* avatar) { - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_COLOR | - LLVertexBuffer::MAP_WEIGHT4; - - renderRigged(avatar, RIGGED_FULLBRIGHT_ALPHA, data_mask); + renderRigged(avatar, RIGGED_FULLBRIGHT_ALPHA); } void LLDrawPoolAvatar::renderRiggedGlow(LLVOAvatar* avatar) { - const U32 data_mask = LLVertexBuffer::MAP_VERTEX | - LLVertexBuffer::MAP_TEXCOORD0 | - LLVertexBuffer::MAP_WEIGHT4; - - renderRigged(avatar, RIGGED_GLOW, data_mask, true); + renderRigged(avatar, RIGGED_GLOW, true); } diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h index 59f9cf7ddb..bab6f01480 100644 --- a/indra/newview/lldrawpoolavatar.h +++ b/indra/newview/lldrawpoolavatar.h @@ -103,6 +103,8 @@ public: void beginRiggedAlpha(); void beginRiggedFullbrightAlpha(); void beginRiggedGlow(); + void beginPostDeferredAlpha(); + void beginDeferredRiggedAlpha(); void endRigid(); void endImpostor(); @@ -114,24 +116,27 @@ public: void endRiggedAlpha(); void endRiggedFullbrightAlpha(); void endRiggedGlow(); + void endPostDeferredAlpha(); + void endDeferredRiggedAlpha(); void beginDeferredImpostor(); void beginDeferredRigid(); void beginDeferredSkinned(); - void beginDeferredRigged(); + void beginDeferredRiggedSimple(); + void beginDeferredRiggedBump(); void endDeferredImpostor(); void endDeferredRigid(); void endDeferredSkinned(); - void endDeferredRigged(); + void endDeferredRiggedSimple(); + void endDeferredRiggedBump(); void updateRiggedFaceVertexBuffer(LLFace* facep, const LLMeshSkinInfo* skin, LLVolume* volume, - const LLVolumeFace& vol_face, - U32 data_mask); + const LLVolumeFace& vol_face); - void renderRigged(LLVOAvatar* avatar, U32 type, const U32 data_mask, bool glow = false); + void renderRigged(LLVOAvatar* avatar, U32 type, bool glow = false); void renderRiggedSimple(LLVOAvatar* avatar); void renderRiggedAlpha(LLVOAvatar* avatar); void renderRiggedFullbrightAlpha(LLVOAvatar* avatar); @@ -139,6 +144,8 @@ public: void renderRiggedShinySimple(LLVOAvatar* avatar); void renderRiggedFullbrightShiny(LLVOAvatar* avatar); void renderRiggedGlow(LLVOAvatar* avatar); + void renderDeferredRiggedSimple(LLVOAvatar* avatar); + void renderDeferredRiggedBump(LLVOAvatar* avatar); /*virtual*/ LLViewerTexture *getDebugTexture(); /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display @@ -154,11 +161,43 @@ public: RIGGED_GLOW, RIGGED_ALPHA, RIGGED_FULLBRIGHT_ALPHA, + RIGGED_DEFERRED_BUMP, + RIGGED_DEFERRED_SIMPLE, NUM_RIGGED_PASSES, RIGGED_UNKNOWN, } eRiggedPass; - + typedef enum + { + RIGGED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_FULLBRIGHT_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_SHINY_MASK = RIGGED_SIMPLE_MASK, + RIGGED_FULLBRIGHT_SHINY_MASK = RIGGED_SIMPLE_MASK, + RIGGED_GLOW_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_ALPHA_MASK = RIGGED_SIMPLE_MASK, + RIGGED_FULLBRIGHT_ALPHA_MASK = RIGGED_FULLBRIGHT_MASK, + RIGGED_DEFERRED_BUMP_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_BINORMAL | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + RIGGED_DEFERRED_SIMPLE_MASK = LLVertexBuffer::MAP_VERTEX | + LLVertexBuffer::MAP_NORMAL | + LLVertexBuffer::MAP_TEXCOORD0 | + LLVertexBuffer::MAP_COLOR | + LLVertexBuffer::MAP_WEIGHT4, + } eRiggedDataMask; + void addRiggedFace(LLFace* facep, U32 type); void removeRiggedFace(LLFace* facep); diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 2f449fa42f..906615ade8 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -591,18 +591,37 @@ void LLDrawPoolBump::renderGroup(LLSpatialGroup* group, U32 type, U32 mask, BOOL // static BOOL LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel) { - LLViewerTexture* bump = NULL; - U8 bump_code = params.mBump; + return bindBumpMap(bump_code, params.mTexture, params.mVSize, channel); +} + +//static +BOOL LLDrawPoolBump::bindBumpMap(LLFace* face, S32 channel) +{ + const LLTextureEntry* te = face->getTextureEntry(); + if (te) + { + U8 bump_code = te->getBumpmap(); + return bindBumpMap(bump_code, face->getTexture(), face->getVirtualSize(), channel); + } + + return FALSE; +} + +//static +BOOL LLDrawPoolBump::bindBumpMap(U8 bump_code, LLViewerTexture* texture, F32 vsize, S32 channel) +{ //Note: texture atlas does not support bump texture now. - LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(params.mTexture) ; + LLViewerFetchedTexture* tex = LLViewerTextureManager::staticCastToFetchedTexture(texture) ; if(!tex) { //if the texture is not a fetched texture return FALSE; } + LLViewerTexture* bump = NULL; + switch( bump_code ) { case BE_NO_BUMP: @@ -616,7 +635,7 @@ BOOL LLDrawPoolBump::bindBumpMap(LLDrawInfo& params, S32 channel) if( bump_code < LLStandardBumpmap::sStandardBumpmapCount ) { bump = gStandardBumpmapList[bump_code].mImage; - gBumpImageList.addTextureStats(bump_code, tex->getID(), params.mVSize); + gBumpImageList.addTextureStats(bump_code, tex->getID(), vsize); } break; } diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h index 89bbefe778..127c9efe85 100644 --- a/indra/newview/lldrawpoolbump.h +++ b/indra/newview/lldrawpoolbump.h @@ -93,7 +93,12 @@ public: /*virtual*/ void endPostDeferredPass(S32 pass); /*virtual*/ void renderPostDeferred(S32 pass); - BOOL bindBumpMap(LLDrawInfo& params, S32 channel = -2); + static BOOL bindBumpMap(LLDrawInfo& params, S32 channel = -2); + static BOOL bindBumpMap(LLFace* face, S32 channel = -2); + +private: + static BOOL bindBumpMap(U8 bump_code, LLViewerTexture* tex, F32 vsize, S32 channel); + }; enum EBumpEffect diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index eafc52748e..e64fdfc0b4 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -114,6 +114,8 @@ LLGLSLShader gDeferredEdgeProgram; LLGLSLShader gDeferredWaterProgram; LLGLSLShader gDeferredDiffuseProgram; LLGLSLShader gDeferredSkinnedDiffuseProgram; +LLGLSLShader gDeferredSkinnedBumpProgram; +LLGLSLShader gDeferredSkinnedAlphaProgram; LLGLSLShader gDeferredBumpProgram; LLGLSLShader gDeferredTerrainProgram; LLGLSLShader gDeferredTreeProgram; @@ -173,6 +175,7 @@ LLViewerShaderMgr::LLViewerShaderMgr() : mShaderList.push_back(&gDeferredLightProgram); mShaderList.push_back(&gDeferredMultiLightProgram); mShaderList.push_back(&gDeferredAlphaProgram); + mShaderList.push_back(&gDeferredSkinnedAlphaProgram); mShaderList.push_back(&gDeferredFullbrightProgram); mShaderList.push_back(&gDeferredPostGIProgram); mShaderList.push_back(&gDeferredEdgeProgram); @@ -591,6 +594,8 @@ void LLViewerShaderMgr::unloadShaders() gDeferredDiffuseProgram.unload(); gDeferredSkinnedDiffuseProgram.unload(); + gDeferredSkinnedBumpProgram.unload(); + gDeferredSkinnedAlphaProgram.unload(); mVertexShaderLevel[SHADER_LIGHTING] = 0; mVertexShaderLevel[SHADER_OBJECT] = 0; @@ -906,6 +911,8 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredTreeProgram.unload(); gDeferredDiffuseProgram.unload(); gDeferredSkinnedDiffuseProgram.unload(); + gDeferredSkinnedBumpProgram.unload(); + gDeferredSkinnedAlphaProgram.unload(); gDeferredBumpProgram.unload(); gDeferredImpostorProgram.unload(); gDeferredTerrainProgram.unload(); @@ -958,6 +965,33 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() success = gDeferredSkinnedDiffuseProgram.createShader(NULL, NULL); } + if (success) + { + gDeferredSkinnedBumpProgram.mName = "Deferred Skinned Bump Shader"; + gDeferredSkinnedBumpProgram.mFeatures.hasObjectSkinning = true; + gDeferredSkinnedBumpProgram.mShaderFiles.clear(); + gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredSkinnedBumpProgram.mShaderFiles.push_back(make_pair("deferred/bumpF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSkinnedBumpProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + success = gDeferredSkinnedBumpProgram.createShader(NULL, NULL); + } + + if (success) + { + gDeferredSkinnedAlphaProgram.mName = "Deferred Skinned Alpha Shader"; + gDeferredSkinnedAlphaProgram.mFeatures.hasObjectSkinning = true; + gDeferredSkinnedAlphaProgram.mFeatures.calculatesLighting = true; + gDeferredSkinnedAlphaProgram.mFeatures.calculatesAtmospherics = true; + gDeferredSkinnedAlphaProgram.mFeatures.hasGamma = true; + gDeferredSkinnedAlphaProgram.mFeatures.hasAtmospherics = true; + gDeferredSkinnedAlphaProgram.mFeatures.hasLighting = true; + gDeferredSkinnedAlphaProgram.mShaderFiles.clear(); + gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaSkinnedV.glsl", GL_VERTEX_SHADER_ARB)); + gDeferredSkinnedAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredSkinnedAlphaProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; + success = gDeferredSkinnedAlphaProgram.createShader(NULL, NULL); + } + if (success) { gDeferredBumpProgram.mName = "Deferred Bump Shader"; @@ -1176,7 +1210,7 @@ BOOL LLViewerShaderMgr::loadShadersDeferred() gDeferredAvatarAlphaProgram.mFeatures.hasLighting = true; gDeferredAvatarAlphaProgram.mShaderFiles.clear(); gDeferredAvatarAlphaProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaV.glsl", GL_VERTEX_SHADER_ARB)); - gDeferredAvatarAlphaProgram.mShaderFiles.push_back(make_pair("deferred/avatarAlphaF.glsl", GL_FRAGMENT_SHADER_ARB)); + gDeferredAvatarAlphaProgram.mShaderFiles.push_back(make_pair("deferred/alphaF.glsl", GL_FRAGMENT_SHADER_ARB)); gDeferredAvatarAlphaProgram.mShaderLevel = mVertexShaderLevel[SHADER_DEFERRED]; success = gDeferredAvatarAlphaProgram.createShader(&mAvatarAttribs, &mAvatarUniforms); } diff --git a/indra/newview/llviewershadermgr.h b/indra/newview/llviewershadermgr.h index bb28cd7ec2..da16a38427 100644 --- a/indra/newview/llviewershadermgr.h +++ b/indra/newview/llviewershadermgr.h @@ -350,6 +350,8 @@ extern LLGLSLShader gDeferredEdgeProgram; extern LLGLSLShader gDeferredWaterProgram; extern LLGLSLShader gDeferredDiffuseProgram; extern LLGLSLShader gDeferredSkinnedDiffuseProgram; +extern LLGLSLShader gDeferredSkinnedBumpProgram; +extern LLGLSLShader gDeferredSkinnedAlphaProgram; extern LLGLSLShader gDeferredBumpProgram; extern LLGLSLShader gDeferredTerrainProgram; extern LLGLSLShader gDeferredTreeProgram; diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index e263f8d937..220634897e 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -3586,6 +3586,21 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_GLOW); } + + if (LLPipeline::sRenderDeferred) + { + if (type != LLDrawPool::POOL_ALPHA && !te->getFullbright()) + { + if (te->getBumpmap()) + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_BUMP); + } + else + { + pool->addRiggedFace(facep, LLDrawPoolAvatar::RIGGED_DEFERRED_SIMPLE); + } + } + } } } -- cgit v1.3 From 744f2e4951fc6fc1a1f08e97fc446e909c1fe39e Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Tue, 4 May 2010 12:14:47 -0500 Subject: Cleanup from review. --- indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl | 3 +-- indra/newview/lldrawpoolavatar.cpp | 2 +- indra/newview/llvovolume.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl index 6f027de6a2..7d18ae5036 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaF.glsl @@ -54,8 +54,7 @@ void main() color.rgb = scaleSoftClip(color.rgb); - gl_FragColor = gl_Color; - //gl_FragColor = color; + gl_FragColor = color; //gl_FragColor = vec4(1,0,1,1); //gl_FragColor = vec4(1,0,1,1)*shadow; diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index ac599caa5b..a49cf8781e 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -1103,7 +1103,7 @@ void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, S32 pass) return; } - if (pass >= 7 && pass <= 9) + if (pass >= 7 && pass < 9) { LLGLEnable blend(GL_BLEND); diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index 220634897e..1ed8da85a8 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -3418,7 +3418,7 @@ void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group) static LLFastTimer::DeclareTimer FTM_REBUILD_VOLUME_VB("Volume"); static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt"); -LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj) +static LLDrawPoolAvatar* get_avatar_drawpool(LLViewerObject* vobj) { LLVOAvatar* avatar = vobj->getAvatar(); -- cgit v1.3 From 33c4d124ce60b2999efb73fc17dc996cb3c2a103 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Thu, 6 May 2010 13:53:52 +0100 Subject: EXT-6912 FIXED Deferred Rendering - shiny objects look like a projection instead of a reflection Disable screen-space shiny while I chew on it some more. (transplanted from bde80560117295f6b3e61f9b47f3bdea826945ca) --- indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl | 3 +++ indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl | 3 +++ indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl | 3 +++ 3 files changed, 9 insertions(+) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index bf5dd4155c..ef61491826 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -286,6 +286,7 @@ void main() float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; + /* // screen-space cheap fakey reflection map // vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz)); @@ -325,6 +326,8 @@ void main() vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); ssshiny *= 0.3; // dampen it even more + */ + vec3 ssshiny = vec3(0,0,0); // add the two types of shiny together col += (ssshiny + dumbshiny) * spec.rgb; diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index 28cfc6322e..bc84720b86 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -285,6 +285,7 @@ void main() float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol_ambocc.r*texture2D(lightFunc, vec2(sa, spec.a)).a; + /* // screen-space cheap fakey reflection map // vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz)); @@ -325,6 +326,8 @@ void main() vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); ssshiny *= 0.3; // dampen it even more + */ + vec3 ssshiny = vec3(0,0,0); // add the two types of shiny together col += (ssshiny + dumbshiny) * spec.rgb; diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 9f94b9e8ea..5298079af7 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -288,6 +288,7 @@ void main() float sa = dot(refnormpersp, vary_light.xyz); vec3 dumbshiny = vary_SunlitColor*scol*texture2D(lightFunc, vec2(sa, spec.a)).a; + /* // screen-space cheap fakey reflection map // vec3 refnorm = normalize(reflect(vec3(0,0,-1), norm.xyz)); @@ -329,6 +330,8 @@ void main() vec3 refprod = vary_SunlitColor * refcol.rgb * refmod; vec3 ssshiny = (refprod * spec.a); ssshiny *= 0.3; // dampen it even more + */ + vec3 ssshiny = vec3(0,0,0); // add the two types of shiny together col += (ssshiny + dumbshiny) * spec.rgb; -- cgit v1.3 From 514186eee31c4bc44e7bc021bb9c526e4138cb07 Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Sun, 9 May 2010 15:02:10 +0100 Subject: Rejig deferred normal-map packing a little, to double its accuracy for free. (transplanted from bf541483d9074c18c6d4f4bd19caf0de82013cbf) --- .../newview/app_settings/shaders/class1/deferred/avatarF.glsl | 3 ++- .../app_settings/shaders/class1/deferred/blurLightF.glsl | 3 ++- indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl | 3 ++- .../app_settings/shaders/class1/deferred/diffuseF.glsl | 3 ++- indra/newview/app_settings/shaders/class1/deferred/giF.glsl | 3 ++- .../shaders/class1/deferred/multiPointLightF.glsl | 3 ++- .../app_settings/shaders/class1/deferred/pointLightF.glsl | 3 ++- .../newview/app_settings/shaders/class1/deferred/postgiF.glsl | 9 +++++---- .../app_settings/shaders/class1/deferred/softenLightF.glsl | 6 ++++-- .../app_settings/shaders/class1/deferred/spotLightF.glsl | 3 ++- .../app_settings/shaders/class1/deferred/sunLightSSAOF.glsl | 3 ++- .../app_settings/shaders/class1/deferred/terrainF.glsl | 3 ++- indra/newview/app_settings/shaders/class1/deferred/treeF.glsl | 3 ++- .../newview/app_settings/shaders/class1/deferred/waterF.glsl | 2 +- .../app_settings/shaders/class2/deferred/blurLightF.glsl | 3 ++- indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl | 11 ++++++++--- .../app_settings/shaders/class2/deferred/multiSpotLightF.glsl | 3 ++- .../app_settings/shaders/class2/deferred/softenLightF.glsl | 7 +++++-- .../app_settings/shaders/class2/deferred/spotLightF.glsl | 3 ++- .../app_settings/shaders/class2/deferred/sunLightF.glsl | 3 ++- .../app_settings/shaders/class2/deferred/sunLightSSAOF.glsl | 3 ++- .../app_settings/shaders/class3/deferred/giDownsampleF.glsl | 10 ++++++---- indra/newview/app_settings/shaders/class3/deferred/giF.glsl | 3 ++- .../app_settings/shaders/class3/deferred/softenLightF.glsl | 7 +++++-- indra/newview/app_settings/shaders/class3/deferred/treeF.glsl | 3 ++- 25 files changed, 70 insertions(+), 36 deletions(-) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl index 75df388941..afbe08a579 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/avatarF.glsl @@ -20,6 +20,7 @@ void main() gl_FragData[0] = vec4(diff.rgb, 0.0); gl_FragData[1] = vec4(0,0,0,0); - gl_FragData[2] = vec4(normalize(vary_normal)*0.5+0.5, 0.0); + vec3 nvn = normalize(vary_normal); + gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl index 0fad5b4b50..d1c5d7cb19 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/blurLightF.glsl @@ -37,7 +37,8 @@ vec4 getPosition(vec2 pos_screen) void main() { - vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm vec3 pos = getPosition(vary_fragcoord.xy).xyz; vec4 ccol = texture2DRect(lightMap, vary_fragcoord.xy).rgba; diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl index 6e38caf5ef..2197744a37 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpF.glsl @@ -24,5 +24,6 @@ void main() gl_FragData[0] = vec4(col, 0.0); gl_FragData[1] = gl_Color.aaaa; // spec //gl_FragData[1] = vec4(vec3(gl_Color.a), gl_Color.a+(1.0-gl_Color.a)*gl_Color.a); // spec - from former class3 - maybe better, but not so well tested - gl_FragData[2] = vec4(normalize(tnorm)*0.5+0.5, 0.0); + vec3 nvn = normalize(tnorm); + gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl index 112103956d..3803119cda 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseF.glsl @@ -15,5 +15,6 @@ void main() gl_FragData[0] = vec4(col, 0.0); gl_FragData[1] = gl_Color.aaaa; // spec //gl_FragData[1] = vec4(vec3(gl_Color.a), gl_Color.a+(1.0-gl_Color.a)*gl_Color.a); // spec - from former class3 - maybe better, but not so well tested - gl_FragData[2] = vec4(normalize(vary_normal)*0.5+0.5, 0.0); + vec3 nvn = normalize(vary_normal); + gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/giF.glsl b/indra/newview/app_settings/shaders/class1/deferred/giF.glsl index b351eec6e5..d4b153c4af 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/giF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/giF.glsl @@ -159,7 +159,8 @@ void main() { vec2 pos_screen = vary_fragcoord.xy; vec4 pos = getPosition(pos_screen); - vec3 norm = texture2DRect(normalMap, pos_screen).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, pos_screen).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm gl_FragData[0].xyz = giAmbient(pos, norm); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl index 797b9e9f3b..e1715eb4f9 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl @@ -53,7 +53,8 @@ void main() discard; } - vec3 norm = normalize(texture2DRect(normalMap, frag.xy).xyz*2.0-1.0); + vec3 norm = texture2DRect(normalMap, frag.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm vec4 spec = texture2DRect(specularRect, frag.xy); vec3 diff = texture2DRect(diffuseRect, frag.xy).rgb; float noise = texture2D(noiseMap, frag.xy/128.0).b; diff --git a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl index 78256e20cc..3aecbc5f23 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/pointLightF.glsl @@ -55,7 +55,8 @@ void main() discard; } - vec3 norm = texture2DRect(normalMap, frag.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, frag.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm float da = dot(norm, lv); if (da < 0.0) { diff --git a/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl b/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl index 9612aee405..bd554c2d84 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/postgiF.glsl @@ -38,10 +38,10 @@ vec4 getPosition(vec2 pos_screen) void main() { - vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm vec3 pos = getPosition(vary_fragcoord.xy).xyz; - vec3 ccol = texture2DRect(giLightMap, vary_fragcoord.xy).rgb; vec2 dlt = kern_scale * delta/(1.0+norm.xy*norm.xy); dlt /= max(-pos.z*dist_factor, 1.0); @@ -51,9 +51,10 @@ void main() for (int i = 0; i < kern_length; i++) { vec2 tc = vary_fragcoord.xy + kern[i].y*dlt; - vec3 sampNorm = texture2DRect(normalMap, tc.xy).xyz*2.0-1.0; + vec3 sampNorm = texture2DRect(normalMap, tc.xy).xyz; + sampNorm = vec3((sampNorm.xy-0.5)*2.0,sampNorm.z); // unpack norm - float d = dot(norm.xyz, sampNorm); + float d = dot(norm.xyz, sampNorm); if (d > 0.8) { diff --git a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl index ef61491826..bef91e735d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/softenLightF.glsl @@ -259,7 +259,8 @@ void main() vec2 tc = vary_fragcoord.xy; float depth = texture2DRect(depthMap, tc.xy).a; vec3 pos = getPosition_d(tc, depth).xyz; - vec3 norm = texture2DRect(normalMap, tc).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, tc).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz; float da = max(dot(norm.xyz, vary_light.xyz), 0.0); @@ -310,7 +311,8 @@ void main() texture2DRect(diffuseRect, ref2d + vec2(-checkoffset, 0.0)).rgb); float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; - vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); + vec3 refn = texture2DRect(normalMap, ref2d).rgb; + refn = normalize(vec3((refn.xy-0.5)*2.0,refn.z)); // unpack norm // figure out how appropriate our guess actually was float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); // darken reflections from points which face away from the reflected ray - our guess was a back-face diff --git a/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl index f320dbb400..1b95b253c3 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/spotLightF.glsl @@ -68,7 +68,8 @@ void main() discard; } - vec3 norm = texture2DRect(normalMap, frag.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, frag.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm norm = normalize(norm); float l_dist = -dot(lv, proj_n); diff --git a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl index 7450817ea7..cdbed4b791 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/sunLightSSAOF.glsl @@ -114,7 +114,8 @@ void main() vec4 pos = getPosition(pos_screen); - vec3 norm = texture2DRect(normalMap, pos_screen).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, pos_screen).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm gl_FragColor[0] = 1.0; gl_FragColor[1] = calcAmbientOcclusion(pos, norm); diff --git a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl index 3cccfb7202..fa0a60c98d 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/terrainF.glsl @@ -29,6 +29,7 @@ void main() gl_FragData[0] = vec4(outColor.rgb, 0.0); gl_FragData[1] = vec4(outColor.rgb*0.2, 0.2); - gl_FragData[2] = vec4(normalize(vary_normal)*0.5+0.5, 0.0); + vec3 nvn = normalize(vary_normal); + gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl index 258acee08c..5b33ea5bfe 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/treeF.glsl @@ -14,5 +14,6 @@ void main() vec4 col = texture2D(diffuseMap, gl_TexCoord[0].xy); gl_FragData[0] = vec4(gl_Color.rgb*col.rgb, col.a <= 0.5 ? 0.0 : 0.005); gl_FragData[1] = vec4(0,0,0,0); - gl_FragData[2] = vec4(normalize(vary_normal)*0.5+0.5, 0.0); + vec3 nvn = normalize(vary_normal); + gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); } diff --git a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl index ceb7e0fb56..361ae8dc84 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/waterF.glsl @@ -137,5 +137,5 @@ void main() gl_FragData[0] = vec4(color.rgb, 0.5); // diffuse gl_FragData[1] = vec4(0.5,0.5,0.5, 0.95); // speccolor*spec, spec - gl_FragData[2] = vec4(screenspacewavef*0.5+0.5, screenspacewavef.z*0.5); // normal, displace + gl_FragData[2] = vec4(screenspacewavef.xy*0.5+0.5, screenspacewavef.z, screenspacewavef.z*0.5); // normalxyz, displace } diff --git a/indra/newview/app_settings/shaders/class2/deferred/blurLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/blurLightF.glsl index 0fad5b4b50..d1c5d7cb19 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/blurLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/blurLightF.glsl @@ -37,7 +37,8 @@ vec4 getPosition(vec2 pos_screen) void main() { - vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm vec3 pos = getPosition(vary_fragcoord.xy).xyz; vec4 ccol = texture2DRect(lightMap, vary_fragcoord.xy).rgba; diff --git a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl index 02beddd43b..e32e9f4b32 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/edgeF.glsl @@ -31,7 +31,8 @@ float getDepth(vec2 pos_screen) void main() { - vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm float depth = getDepth(vary_fragcoord.xy); vec2 tc = vary_fragcoord.xy; @@ -46,8 +47,12 @@ void main() de = step(depth_cutoff, de); vec2 ne; - ne.x = dot(texture2DRect(normalMap, tc+vec2(-sc,-sc)).rgb*2.0-1.0, norm); - ne.y = dot(texture2DRect(normalMap, tc+vec2(sc,sc)).rgb*2.0-1.0, norm); + vec3 nexnorm = texture2DRect(normalMap, tc+vec2(-sc,-sc)).rgb; + nexnorm = vec3((nexnorm.xy-0.5)*2.0,nexnorm.z); // unpack norm + ne.x = dot(nexnorm, norm); + vec3 neynorm = texture2DRect(normalMap, tc+vec2(sc,sc)).rgb; + neynorm = vec3((neynorm.xy-0.5)*2.0,neynorm.z); // unpack norm + ne.y = dot(neynorm, norm); ne = 1.0-ne; diff --git a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl index 5308e5bb1e..22ffb58c63 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/multiSpotLightF.glsl @@ -129,7 +129,8 @@ void main() shadow = min(sh[proj_shadow_idx]+shadow_fade, 1.0); } - vec3 norm = texture2DRect(normalMap, frag.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, frag.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm norm = normalize(norm); float l_dist = -dot(lv, proj_n); diff --git a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl index bc84720b86..fd6ae2b960 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/softenLightF.glsl @@ -258,7 +258,8 @@ void main() vec2 tc = vary_fragcoord.xy; float depth = texture2DRect(depthMap, tc.xy).a; vec3 pos = getPosition_d(tc, depth).xyz; - vec3 norm = texture2DRect(normalMap, tc).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, tc).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz; float da = max(dot(norm.xyz, vary_light.xyz), 0.0); @@ -310,7 +311,9 @@ void main() float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; float refshad = texture2DRect(lightMap, ref2d).r; - vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); + vec3 refn = texture2DRect(normalMap, ref2d).rgb; + refn = vec3((refn.xy-0.5)*2.0,refn.z); // unpack norm + refn = normalize(refn); // figure out how appropriate our guess actually was float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); // darken reflections from points which face away from the reflected ray - our guess was a back-face diff --git a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl index eeaecc157f..8a90199b7c 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/spotLightF.glsl @@ -82,7 +82,8 @@ void main() discard; } - vec3 norm = texture2DRect(normalMap, frag.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, frag.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm norm = normalize(norm); float l_dist = -dot(lv, proj_n); diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl index 794c4d2761..7423347346 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightF.glsl @@ -104,8 +104,9 @@ void main() vec4 pos = getPosition(pos_screen); vec4 nmap4 = texture2DRect(normalMap, pos_screen); + nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm float displace = nmap4.w; - vec3 norm = nmap4.xyz*2.0-1.0; + vec3 norm = nmap4.xyz; /*if (pos.z == 0.0) // do nothing for sky *FIX: REMOVE THIS IF/WHEN THE POSITION MAP IS BEING USED AS A STENCIL { diff --git a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl index a0dfc96f14..4e33a1af45 100644 --- a/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl +++ b/indra/newview/app_settings/shaders/class2/deferred/sunLightSSAOF.glsl @@ -164,8 +164,9 @@ void main() vec4 pos = getPosition(pos_screen); vec4 nmap4 = texture2DRect(normalMap, pos_screen); + nmap4 = vec4((nmap4.xy-0.5)*2.0,nmap4.z,nmap4.w); // unpack norm float displace = nmap4.w; - vec3 norm = nmap4.xyz*2.0-1.0; + vec3 norm = nmap4.xyz; /*if (pos.z == 0.0) // do nothing for sky *FIX: REMOVE THIS IF/WHEN THE POSITION MAP IS BEING USED AS A STENCIL { diff --git a/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl b/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl index 7325825d6d..66606233cd 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/giDownsampleF.glsl @@ -44,7 +44,8 @@ float getDepth(vec2 pos_screen) void main() { - vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, vary_fragcoord.xy).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm float depth = getDepth(vary_fragcoord.xy); vec3 ccol = texture2DRect(giLightMap, vary_fragcoord.xy).rgb; @@ -56,9 +57,10 @@ void main() for (int i = 0; i < kern_length; i++) { vec2 tc = vary_fragcoord.xy + kern[i].y*dlt; - vec3 sampNorm = texture2DRect(normalMap, tc.xy).xyz*2.0-1.0; - - float d = dot(norm.xyz, sampNorm); + vec3 sampNorm = texture2DRect(normalMap, tc.xy).xyz; + sampNorm = vec3((sampNorm.xy-0.5)*2.0,sampNorm.z); // unpack norm + + float d = dot(norm.xyz, sampNorm); if (d > 0.5) { diff --git a/indra/newview/app_settings/shaders/class3/deferred/giF.glsl b/indra/newview/app_settings/shaders/class3/deferred/giF.glsl index 939710cb56..1b8354dbd1 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/giF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/giF.glsl @@ -178,7 +178,8 @@ void main() float rad = gi_range*0.5; - vec3 norm = texture2DRect(normalMap, pos_screen).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, pos_screen).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm float dist = max(length(pos.xyz)-rad, 0.0); float da = clamp(1.0-dist/rad, 0.0, 1.0); diff --git a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl index 5298079af7..c88edd0a60 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/softenLightF.glsl @@ -259,7 +259,8 @@ void main() vec2 tc = vary_fragcoord.xy; float depth = texture2DRect(depthMap, tc.xy).a; vec3 pos = getPosition_d(tc, depth).xyz; - vec3 norm = texture2DRect(normalMap, tc).xyz*2.0-1.0; + vec3 norm = texture2DRect(normalMap, tc).xyz; + norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm //vec3 nz = texture2D(noiseMap, vary_fragcoord.xy/128.0).xyz; float da = max(dot(norm.xyz, vary_light.xyz), 0.0); @@ -314,7 +315,9 @@ void main() float refdepth = texture2DRect(depthMap, ref2d).a; vec3 refpos = getPosition_d(ref2d, refdepth).xyz; float refshad = texture2DRect(lightMap, ref2d).r; - vec3 refn = normalize(texture2DRect(normalMap, ref2d).rgb * 2.0 - 1.0); + vec3 refn = texture2DRect(normalMap, ref2d).rgb; + refn = vec3((refn.xy-0.5)*2.0,refn.z); // unpack norm + refn = normalize(refn); // figure out how appropriate our guess actually was float refapprop = max(0.0, dot(-refnorm, normalize(pos - refpos))); // darken reflections from points which face away from the reflected ray - our guess was a back-face diff --git a/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl index 258acee08c..5b33ea5bfe 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/treeF.glsl @@ -14,5 +14,6 @@ void main() vec4 col = texture2D(diffuseMap, gl_TexCoord[0].xy); gl_FragData[0] = vec4(gl_Color.rgb*col.rgb, col.a <= 0.5 ? 0.0 : 0.005); gl_FragData[1] = vec4(0,0,0,0); - gl_FragData[2] = vec4(normalize(vary_normal)*0.5+0.5, 0.0); + vec3 nvn = normalize(vary_normal); + gl_FragData[2] = vec4(nvn.xy * 0.5 + 0.5, nvn.z, 0.0); } -- cgit v1.3 From b14e52a5a3f29ad250b4e36ba8bcccf494ff43bc Mon Sep 17 00:00:00 2001 From: Tofu Linden Date: Tue, 11 May 2010 20:39:43 +0100 Subject: add an accidentally-dropped normalize from the bf541483d907 / normpacking-rejig (transplanted from 9e23eb15550830b5efe1012c8169339931ec5f32) --- indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl index e1715eb4f9..b494b521ca 100644 --- a/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl +++ b/indra/newview/app_settings/shaders/class1/deferred/multiPointLightF.glsl @@ -55,6 +55,7 @@ void main() vec3 norm = texture2DRect(normalMap, frag.xy).xyz; norm = vec3((norm.xy-0.5)*2.0,norm.z); // unpack norm + norm = normalize(norm); vec4 spec = texture2DRect(specularRect, frag.xy); vec3 diff = texture2DRect(diffuseRect, frag.xy).rgb; float noise = texture2D(noiseMap, frag.xy/128.0).b; -- cgit v1.3 From 71de5f622a7917f78823a7e7840194e1b0f8f070 Mon Sep 17 00:00:00 2001 From: Dave Parks Date: Thu, 26 Aug 2010 14:23:12 -0500 Subject: Add missing files from viewer-experimental --- indra/cmake/GLOD.cmake | 9 + indra/cmake/LLConvexDecomposition.cmake | 16 + indra/llmath/llmatrix3a.cpp | 140 + indra/llmath/llmatrix3a.h | 134 + indra/llmath/llmatrix3a.inl | 125 + indra/llmath/llmatrix4a.h | 149 + indra/llmath/llquaternion2.h | 111 + indra/llmath/llquaternion2.inl | 108 + indra/llmath/llsimdmath.h | 95 + indra/llmath/llsimdtypes.h | 130 + indra/llmath/llsimdtypes.inl | 163 + indra/llmath/llvector4a.cpp | 228 ++ indra/llmath/llvector4a.h | 331 ++ indra/llmath/llvector4a.inl | 599 ++++ indra/llmath/llvector4logical.h | 130 + indra/llmath/llvolumeoctree.cpp | 208 ++ indra/llmath/llvolumeoctree.h | 138 + indra/llprimitive/llmodel.cpp | 1695 ++++++++++ indra/llprimitive/llmodel.h | 179 ++ .../shaders/class1/avatar/objectSkinV.glsl | 30 + .../shaders/class1/deferred/alphaSkinnedV.glsl | 75 + .../shaders/class1/deferred/attachmentShadowF.glsl | 16 + .../shaders/class1/deferred/attachmentShadowV.glsl | 25 + .../shaders/class1/deferred/bumpSkinnedV.glsl | 37 + .../shaders/class1/deferred/diffuseSkinnedV.glsl | 33 + .../lighting/lightFullbrightShinyWaterF.glsl | 15 + .../class1/objects/fullbrightShinySkinnedV.glsl | 39 + .../class1/objects/fullbrightShinyWaterF.glsl | 13 + .../shaders/class1/objects/fullbrightSkinnedV.glsl | 37 + .../class1/objects/shinySimpleSkinnedV.glsl | 39 + .../shaders/class1/objects/simpleSkinnedV.glsl | 39 + .../shaders/class2/deferred/alphaSkinnedV.glsl | 84 + .../lighting/lightFullbrightShinyWaterF.glsl | 29 + indra/newview/llfloaterimportcollada.cpp | 1116 +++++++ indra/newview/llfloaterimportcollada.h | 143 + indra/newview/llfloatermodelpreview.cpp | 3382 ++++++++++++++++++++ indra/newview/llfloatermodelpreview.h | 275 ++ indra/newview/llmeshreduction.cpp | 291 ++ indra/newview/llmeshreduction.h | 59 + indra/newview/llmeshrepository.cpp | 2676 ++++++++++++++++ indra/newview/llmeshrepository.h | 471 +++ 41 files changed, 13612 insertions(+) create mode 100644 indra/cmake/GLOD.cmake create mode 100644 indra/cmake/LLConvexDecomposition.cmake create mode 100644 indra/llmath/llmatrix3a.cpp create mode 100644 indra/llmath/llmatrix3a.h create mode 100644 indra/llmath/llmatrix3a.inl create mode 100644 indra/llmath/llmatrix4a.h create mode 100644 indra/llmath/llquaternion2.h create mode 100644 indra/llmath/llquaternion2.inl create mode 100644 indra/llmath/llsimdmath.h create mode 100644 indra/llmath/llsimdtypes.h create mode 100644 indra/llmath/llsimdtypes.inl create mode 100644 indra/llmath/llvector4a.cpp create mode 100644 indra/llmath/llvector4a.h create mode 100644 indra/llmath/llvector4a.inl create mode 100644 indra/llmath/llvector4logical.h create mode 100644 indra/llmath/llvolumeoctree.cpp create mode 100644 indra/llmath/llvolumeoctree.h create mode 100644 indra/llprimitive/llmodel.cpp create mode 100644 indra/llprimitive/llmodel.h create mode 100644 indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl create mode 100644 indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl create mode 100644 indra/newview/llfloaterimportcollada.cpp create mode 100644 indra/newview/llfloaterimportcollada.h create mode 100644 indra/newview/llfloatermodelpreview.cpp create mode 100644 indra/newview/llfloatermodelpreview.h create mode 100644 indra/newview/llmeshreduction.cpp create mode 100644 indra/newview/llmeshreduction.h create mode 100644 indra/newview/llmeshrepository.cpp create mode 100644 indra/newview/llmeshrepository.h (limited to 'indra/newview/app_settings/shaders/class1') diff --git a/indra/cmake/GLOD.cmake b/indra/cmake/GLOD.cmake new file mode 100644 index 0000000000..77221d55ed --- /dev/null +++ b/indra/cmake/GLOD.cmake @@ -0,0 +1,9 @@ +# -*- cmake -*- +include(Prebuilt) + +if (NOT STANDALONE) + use_prebuilt_binary(GLOD) +endif (NOT STANDALONE) + +set(GLOD_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include) +set(GLOD_LIBRARIES glod) diff --git a/indra/cmake/LLConvexDecomposition.cmake b/indra/cmake/LLConvexDecomposition.cmake new file mode 100644 index 0000000000..ae9dc3c17a --- /dev/null +++ b/indra/cmake/LLConvexDecomposition.cmake @@ -0,0 +1,16 @@ +# -*- cmake -*- +include(Prebuilt) + +set(LLCONVEXDECOMP_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) + +if (INSTALL_PROPRIETARY AND NOT STANDALONE) + use_prebuilt_binary(llconvexdecomposition) + if (WINDOWS) + set(LLCONVEXDECOMP_LIBRARY llconvexdecomposition) + else (WINDOWS) + set(LLCONVEXDECOMP_LIBRARY llconvexdecompositionstub) + endif (WINDOWS) +else (INSTALL_PROPRIETARY AND NOT STANDALONE) + use_prebuilt_binary(llconvexdecompositionstub) + set(LLCONVEXDECOMP_LIBRARY llconvexdecompositionstub) +endif (INSTALL_PROPRIETARY AND NOT STANDALONE) diff --git a/indra/llmath/llmatrix3a.cpp b/indra/llmath/llmatrix3a.cpp new file mode 100644 index 0000000000..b7468f4914 --- /dev/null +++ b/indra/llmath/llmatrix3a.cpp @@ -0,0 +1,140 @@ +/** + * @file llvector4a.cpp + * @brief SIMD vector implementation + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llmath.h" + +static LL_ALIGN_16(const F32 M_IDENT_3A[12]) = + { 1.f, 0.f, 0.f, 0.f, // Column 1 + 0.f, 1.f, 0.f, 0.f, // Column 2 + 0.f, 0.f, 1.f, 0.f }; // Column 3 + +extern const LLMatrix3a LL_M3A_IDENTITY = *reinterpret_cast (M_IDENT_3A); + +void LLMatrix3a::setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs ) +{ + const LLVector4a col0 = lhs.getColumn(0); + const LLVector4a col1 = lhs.getColumn(1); + const LLVector4a col2 = lhs.getColumn(2); + + for ( int i = 0; i < 3; i++ ) + { + LLVector4a xxxx = _mm_load_ss( rhs.mColumns[i].getF32ptr() ); + xxxx.splat<0>( xxxx ); + xxxx.mul( col0 ); + + { + LLVector4a yyyy = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 1 ); + yyyy.splat<0>( yyyy ); + yyyy.mul( col1 ); + xxxx.add( yyyy ); + } + + { + LLVector4a zzzz = _mm_load_ss( rhs.mColumns[i].getF32ptr() + 2 ); + zzzz.splat<0>( zzzz ); + zzzz.mul( col2 ); + xxxx.add( zzzz ); + } + + xxxx.store4a( mColumns[i].getF32ptr() ); + } + +} + +/*static */void LLMatrix3a::batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst ) +{ + const LLVector4a col0 = xform.getColumn(0); + const LLVector4a col1 = xform.getColumn(1); + const LLVector4a col2 = xform.getColumn(2); + const LLVector4a* maxAddr = src + numVectors; + + if ( numVectors & 0x1 ) + { + LLVector4a xxxx = _mm_load_ss( (const F32*)src ); + LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 ); + LLVector4a zzzz = _mm_load_ss( (const F32*)src + 2 ); + xxxx.splat<0>( xxxx ); + yyyy.splat<0>( yyyy ); + zzzz.splat<0>( zzzz ); + xxxx.mul( col0 ); + yyyy.mul( col1 ); + zzzz.mul( col2 ); + xxxx.add( yyyy ); + xxxx.add( zzzz ); + xxxx.store4a( (F32*)dst ); + src++; + dst++; + } + + + numVectors >>= 1; + while ( src < maxAddr ) + { + _mm_prefetch( (const char*)(src + 32 ), _MM_HINT_NTA ); + _mm_prefetch( (const char*)(dst + 32), _MM_HINT_NTA ); + LLVector4a xxxx = _mm_load_ss( (const F32*)src ); + LLVector4a xxxx1= _mm_load_ss( (const F32*)(src + 1) ); + + xxxx.splat<0>( xxxx ); + xxxx1.splat<0>( xxxx1 ); + xxxx.mul( col0 ); + xxxx1.mul( col0 ); + + { + LLVector4a yyyy = _mm_load_ss( (const F32*)src + 1 ); + LLVector4a yyyy1 = _mm_load_ss( (const F32*)(src + 1) + 1); + yyyy.splat<0>( yyyy ); + yyyy1.splat<0>( yyyy1 ); + yyyy.mul( col1 ); + yyyy1.mul( col1 ); + xxxx.add( yyyy ); + xxxx1.add( yyyy1 ); + } + + { + LLVector4a zzzz = _mm_load_ss( (const F32*)(src) + 2 ); + LLVector4a zzzz1 = _mm_load_ss( (const F32*)(++src) + 2 ); + zzzz.splat<0>( zzzz ); + zzzz1.splat<0>( zzzz1 ); + zzzz.mul( col2 ); + zzzz1.mul( col2 ); + xxxx.add( zzzz ); + xxxx1.add( zzzz1 ); + } + + xxxx.store4a(dst->getF32ptr()); + src++; + dst++; + + xxxx1.store4a((F32*)dst++); + } +} diff --git a/indra/llmath/llmatrix3a.h b/indra/llmath/llmatrix3a.h new file mode 100644 index 0000000000..56327f9f6d --- /dev/null +++ b/indra/llmath/llmatrix3a.h @@ -0,0 +1,134 @@ +/** + * @file llmatrix3a.h + * @brief LLMatrix3a class header file - memory aligned and vectorized 3x3 matrix + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLMATRIX3A_H +#define LL_LLMATRIX3A_H + +///////////////////////////// +// LLMatrix3a, LLRotation +///////////////////////////// +// This class stores a 3x3 (technically 4x3) matrix in column-major order +///////////////////////////// +///////////////////////////// +// These classes are intentionally minimal right now. If you need additional +// functionality, please contact someone with SSE experience (e.g., Falcon or +// Huseby). +///////////////////////////// + +// LLMatrix3a is the base class for LLRotation, which should be used instead any time you're dealing with a +// rotation matrix. +class LLMatrix3a +{ +public: + + // Utility function for quickly transforming an array of LLVector4a's + // For transforming a single LLVector4a, see LLVector4a::setRotated + static void batchTransform( const LLMatrix3a& xform, const LLVector4a* src, int numVectors, LLVector4a* dst ); + + // Utility function to obtain the identity matrix + static inline const LLMatrix3a& getIdentity(); + + ////////////////////////// + // Ctors + ////////////////////////// + + // Ctor + LLMatrix3a() {} + + // Ctor for setting by columns + inline LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 ); + + ////////////////////////// + // Get/Set + ////////////////////////// + + // Loads from an LLMatrix3 + inline void loadu(const LLMatrix3& src); + + // Set rows + inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2); + + // Set columns + inline void setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2); + + // Get the read-only access to a specified column. Valid columns are 0-2, but the + // function is unchecked. You've been warned. + inline const LLVector4a& getColumn(const U32 column) const; + + ///////////////////////// + // Matrix modification + ///////////////////////// + + // Set this matrix to the product of lhs and rhs ( this = lhs * rhs ) + void setMul( const LLMatrix3a& lhs, const LLMatrix3a& rhs ); + + // Set this matrix to the transpose of src + inline void setTranspose(const LLMatrix3a& src); + + // Set this matrix to a*w + b*(1-w) + inline void setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w); + + ///////////////////////// + // Matrix inspection + ///////////////////////// + + // Sets all 4 elements in 'dest' to the determinant of this matrix. + // If you will be using the determinant in subsequent ops with LLVector4a, use this version + inline void getDeterminant( LLVector4a& dest ) const; + + // Returns the determinant as an LLSimdScalar. Use this if you will be using the determinant + // primary for scalar operations. + inline LLSimdScalar getDeterminant() const; + + // Returns nonzero if rows 0-2 and colums 0-2 contain no NaN or INF values. Row 3 is ignored + inline LLBool32 isFinite() const; + + // Returns true if this matrix is equal to 'rhs' up to 'tolerance' + inline bool isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + +protected: + + LLVector4a mColumns[3]; + +}; + +class LLRotation : public LLMatrix3a +{ +public: + + LLRotation() {} + + // Returns true if this rotation is orthonormal with det ~= 1 + inline bool isOkRotation() const; +}; + +#endif diff --git a/indra/llmath/llmatrix3a.inl b/indra/llmath/llmatrix3a.inl new file mode 100644 index 0000000000..65fd949f78 --- /dev/null +++ b/indra/llmath/llmatrix3a.inl @@ -0,0 +1,125 @@ +/** + * @file llmatrix3a.inl + * @brief LLMatrix3a inline definitions + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llmatrix3a.h" +#include "m3math.h" + +inline LLMatrix3a::LLMatrix3a( const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2 ) +{ + setColumns( c0, c1, c2 ); +} + +inline void LLMatrix3a::loadu(const LLMatrix3& src) +{ + mColumns[0].load3(src.mMatrix[0]); + mColumns[1].load3(src.mMatrix[1]); + mColumns[2].load3(src.mMatrix[2]); +} + +inline void LLMatrix3a::setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2) +{ + mColumns[0] = r0; + mColumns[1] = r1; + mColumns[2] = r2; + setTranspose( *this ); +} + +inline void LLMatrix3a::setColumns(const LLVector4a& c0, const LLVector4a& c1, const LLVector4a& c2) +{ + mColumns[0] = c0; + mColumns[1] = c1; + mColumns[2] = c2; +} + +inline void LLMatrix3a::setTranspose(const LLMatrix3a& src) +{ + const LLQuad srcCol0 = src.mColumns[0]; + const LLQuad srcCol1 = src.mColumns[1]; + const LLQuad unpacklo = _mm_unpacklo_ps( srcCol0, srcCol1 ); + mColumns[0] = _mm_movelh_ps( unpacklo, src.mColumns[2] ); + mColumns[1] = _mm_shuffle_ps( _mm_movehl_ps( srcCol0, unpacklo ), src.mColumns[2], _MM_SHUFFLE(0, 1, 1, 0) ); + mColumns[2] = _mm_shuffle_ps( _mm_unpackhi_ps( srcCol0, srcCol1 ), src.mColumns[2], _MM_SHUFFLE(0, 2, 1, 0) ); +} + +inline const LLVector4a& LLMatrix3a::getColumn(const U32 column) const +{ + llassert( column < 3 ); + return mColumns[column]; +} + +inline void LLMatrix3a::setLerp(const LLMatrix3a& a, const LLMatrix3a& b, F32 w) +{ + mColumns[0].setLerp( a.mColumns[0], b.mColumns[0], w ); + mColumns[1].setLerp( a.mColumns[1], b.mColumns[1], w ); + mColumns[2].setLerp( a.mColumns[2], b.mColumns[2], w ); +} + +inline LLBool32 LLMatrix3a::isFinite() const +{ + return mColumns[0].isFinite3() && mColumns[1].isFinite3() && mColumns[2].isFinite3(); +} + +inline void LLMatrix3a::getDeterminant( LLVector4a& dest ) const +{ + LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] ); + dest.setAllDot3( col1xcol2, mColumns[0] ); +} + +inline LLSimdScalar LLMatrix3a::getDeterminant() const +{ + LLVector4a col1xcol2; col1xcol2.setCross3( mColumns[1], mColumns[2] ); + return col1xcol2.dot3( mColumns[0] ); +} + +inline bool LLMatrix3a::isApproximatelyEqual( const LLMatrix3a& rhs, F32 tolerance /*= F_APPROXIMATELY_ZERO*/ ) const +{ + return rhs.getColumn(0).equals3(mColumns[0], tolerance) + && rhs.getColumn(1).equals3(mColumns[1], tolerance) + && rhs.getColumn(2).equals3(mColumns[2], tolerance); +} + +inline const LLMatrix3a& LLMatrix3a::getIdentity() +{ + extern const LLMatrix3a LL_M3A_IDENTITY; + return LL_M3A_IDENTITY; +} + +inline bool LLRotation::isOkRotation() const +{ + LLMatrix3a transpose; transpose.setTranspose( *this ); + LLMatrix3a product; product.setMul( *this, transpose ); + + LLSimdScalar detMinusOne = getDeterminant() - 1.f; + + return product.isApproximatelyEqual( LLMatrix3a::getIdentity() ) && (detMinusOne.getAbs() < F_APPROXIMATELY_ZERO); +} + diff --git a/indra/llmath/llmatrix4a.h b/indra/llmath/llmatrix4a.h new file mode 100644 index 0000000000..0ead045d04 --- /dev/null +++ b/indra/llmath/llmatrix4a.h @@ -0,0 +1,149 @@ +/** + * @file llmatrix4a.h + * @brief LLMatrix4a class header file - memory aligned and vectorized 4x4 matrix + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLMATRIX4A_H +#define LL_LLMATRIX4A_H + +#include "llvector4a.h" +#include "m4math.h" +#include "m3math.h" + +class LLMatrix4a +{ +public: + LLVector4a mMatrix[4]; + + inline void clear() + { + mMatrix[0].clear(); + mMatrix[1].clear(); + mMatrix[2].clear(); + mMatrix[3].clear(); + } + + inline void loadu(const LLMatrix4& src) + { + mMatrix[0] = _mm_loadu_ps(src.mMatrix[0]); + mMatrix[1] = _mm_loadu_ps(src.mMatrix[1]); + mMatrix[2] = _mm_loadu_ps(src.mMatrix[2]); + mMatrix[3] = _mm_loadu_ps(src.mMatrix[3]); + + } + + inline void loadu(const LLMatrix3& src) + { + mMatrix[0].load3(src.mMatrix[0]); + mMatrix[1].load3(src.mMatrix[1]); + mMatrix[2].load3(src.mMatrix[2]); + mMatrix[3].set(0,0,0,1.f); + } + + inline void add(const LLMatrix4a& rhs) + { + mMatrix[0].add(rhs.mMatrix[0]); + mMatrix[1].add(rhs.mMatrix[1]); + mMatrix[2].add(rhs.mMatrix[2]); + mMatrix[3].add(rhs.mMatrix[3]); + } + + inline void setRows(const LLVector4a& r0, const LLVector4a& r1, const LLVector4a& r2) + { + mMatrix[0] = r0; + mMatrix[1] = r1; + mMatrix[2] = r2; + } + + inline void setMul(const LLMatrix4a& m, const F32 s) + { + mMatrix[0].setMul(m.mMatrix[0], s); + mMatrix[1].setMul(m.mMatrix[1], s); + mMatrix[2].setMul(m.mMatrix[2], s); + mMatrix[3].setMul(m.mMatrix[3], s); + } + + inline void setLerp(const LLMatrix4a& a, const LLMatrix4a& b, F32 w) + { + LLVector4a d0,d1,d2,d3; + d0.setSub(b.mMatrix[0], a.mMatrix[0]); + d1.setSub(b.mMatrix[1], a.mMatrix[1]); + d2.setSub(b.mMatrix[2], a.mMatrix[2]); + d3.setSub(b.mMatrix[3], a.mMatrix[3]); + + // this = a + d*w + + d0.mul(w); + d1.mul(w); + d2.mul(w); + d3.mul(w); + + mMatrix[0].setAdd(a.mMatrix[0],d0); + mMatrix[1].setAdd(a.mMatrix[1],d1); + mMatrix[2].setAdd(a.mMatrix[2],d2); + mMatrix[3].setAdd(a.mMatrix[3],d3); + } + + inline void rotate(const LLVector4a& v, LLVector4a& res) + { + res = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); + res.mul(mMatrix[0]); + + LLVector4a y; + y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); + y.mul(mMatrix[1]); + + LLVector4a z; + z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); + z.mul(mMatrix[2]); + + res.add(y); + res.add(z); + } + + inline void affineTransform(const LLVector4a& v, LLVector4a& res) + { + LLVector4a x,y,z; + + x = _mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, 0)); + y = _mm_shuffle_ps(v, v, _MM_SHUFFLE(1, 1, 1, 1)); + z = _mm_shuffle_ps(v, v, _MM_SHUFFLE(2, 2, 2, 2)); + + x.mul(mMatrix[0]); + y.mul(mMatrix[1]); + z.mul(mMatrix[2]); + + x.add(y); + z.add(mMatrix[3]); + res.setAdd(x,z); + } +}; + +#endif diff --git a/indra/llmath/llquaternion2.h b/indra/llmath/llquaternion2.h new file mode 100644 index 0000000000..dbb4afe312 --- /dev/null +++ b/indra/llmath/llquaternion2.h @@ -0,0 +1,111 @@ +/** + * @file llquaternion2.h + * @brief LLQuaternion2 class header file - SIMD-enabled quaternion class + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_QUATERNION2_H +#define LL_QUATERNION2_H + +///////////////////////////// +// LLQuaternion2 +///////////////////////////// +// This class stores a quaternion x*i + y*j + z*k + w in order +// (i.e., w in high order element of vector) +///////////////////////////// +///////////////////////////// +// These classes are intentionally minimal right now. If you need additional +// functionality, please contact someone with SSE experience (e.g., Falcon or +// Huseby). +///////////////////////////// +#include "llquaternion.h" + +class LLQuaternion2 +{ +public: + + ////////////////////////// + // Ctors + ////////////////////////// + + // Ctor + LLQuaternion2() {} + + // Ctor from LLQuaternion + explicit LLQuaternion2( const class LLQuaternion& quat ); + + ////////////////////////// + // Get/Set + ////////////////////////// + + // Load from an LLQuaternion + inline void operator=( const LLQuaternion& quat ) + { + mQ.loadua( quat.mQ ); + } + + // Return the internal LLVector4a representation of the quaternion + inline const LLVector4a& getVector4a() const; + inline LLVector4a& getVector4aRw(); + + ///////////////////////// + // Quaternion modification + ///////////////////////// + + // Set this quaternion to the conjugate of src + inline void setConjugate(const LLQuaternion2& src); + + // Renormalizes the quaternion. Assumes it has nonzero length. + inline void normalize(); + + // Quantize this quaternion to 8 bit precision + inline void quantize8(); + + // Quantize this quaternion to 16 bit precision + inline void quantize16(); + + ///////////////////////// + // Quaternion inspection + ///////////////////////// + + // Return true if this quaternion is equal to 'rhs'. + // Note! Quaternions exhibit "double-cover", so any rotation has two equally valid + // quaternion representations and they will NOT compare equal. + inline bool equals(const LLQuaternion2& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + + // Return true if all components are finite and the quaternion is normalized + inline bool isOkRotation() const; + +protected: + + LLVector4a mQ; + +}; + +#endif diff --git a/indra/llmath/llquaternion2.inl b/indra/llmath/llquaternion2.inl new file mode 100644 index 0000000000..9a4274d6a4 --- /dev/null +++ b/indra/llmath/llquaternion2.inl @@ -0,0 +1,108 @@ +/** + * @file llquaternion2.inl + * @brief LLQuaternion2 inline definitions + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llquaternion2.h" + +static const LLQuad LL_V4A_PLUS_ONE = {1.f, 1.f, 1.f, 1.f}; +static const LLQuad LL_V4A_MINUS_ONE = {-1.f, -1.f, -1.f, -1.f}; + +// Ctor from LLQuaternion +inline LLQuaternion2::LLQuaternion2( const LLQuaternion& quat ) +{ + mQ.set(quat.mQ[VX], quat.mQ[VY], quat.mQ[VZ], quat.mQ[VW]); +} + +////////////////////////// +// Get/Set +////////////////////////// + +// Return the internal LLVector4a representation of the quaternion +inline const LLVector4a& LLQuaternion2::getVector4a() const +{ + return mQ; +} + +inline LLVector4a& LLQuaternion2::getVector4aRw() +{ + return mQ; +} + +///////////////////////// +// Quaternion modification +///////////////////////// + +// Set this quaternion to the conjugate of src +inline void LLQuaternion2::setConjugate(const LLQuaternion2& src) +{ + static LL_ALIGN_16( const U32 F_QUAT_INV_MASK_4A[4] ) = { 0x80000000, 0x80000000, 0x80000000, 0x00000000 }; + mQ = _mm_xor_ps(src.mQ, *reinterpret_cast(&F_QUAT_INV_MASK_4A)); +} + +// Renormalizes the quaternion. Assumes it has nonzero length. +inline void LLQuaternion2::normalize() +{ + mQ.normalize4(); +} + +// Quantize this quaternion to 8 bit precision +inline void LLQuaternion2::quantize8() +{ + mQ.quantize8( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE ); + normalize(); +} + +// Quantize this quaternion to 16 bit precision +inline void LLQuaternion2::quantize16() +{ + mQ.quantize16( LL_V4A_MINUS_ONE, LL_V4A_PLUS_ONE ); + normalize(); +} + + +///////////////////////// +// Quaternion inspection +///////////////////////// + +// Return true if this quaternion is equal to 'rhs'. +// Note! Quaternions exhibit "double-cover", so any rotation has two equally valid +// quaternion representations and they will NOT compare equal. +inline bool LLQuaternion2::equals(const LLQuaternion2 &rhs, F32 tolerance/* = F_APPROXIMATELY_ZERO*/) const +{ + return mQ.equals4(rhs.mQ, tolerance); +} + +// Return true if all components are finite and the quaternion is normalized +inline bool LLQuaternion2::isOkRotation() const +{ + return mQ.isFinite4() && mQ.isNormalized4(); +} + diff --git a/indra/llmath/llsimdmath.h b/indra/llmath/llsimdmath.h new file mode 100644 index 0000000000..9377bfdb53 --- /dev/null +++ b/indra/llmath/llsimdmath.h @@ -0,0 +1,95 @@ +/** + * @file llsimdmath.h + * @brief Common header for SIMD-based math library (llvector4a, llmatrix3a, etc.) + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_SIMD_MATH_H +#define LL_SIMD_MATH_H + +#ifndef LLMATH_H +#error "Please include llmath.h before this file." +#endif + +#if ( ( LL_DARWIN || LL_LINUX ) && !(__SSE2__) ) || ( LL_WINDOWS && ( _M_IX86_FP < 2 ) ) +#error SSE2 not enabled. LLVector4a and related class will not compile. +#endif + +template T* LL_NEXT_ALIGNED_ADDRESS(T* address) +{ + return reinterpret_cast( + (reinterpret_cast(address) + 0xF) & ~0xF); +} + +template T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) +{ + return reinterpret_cast( + (reinterpret_cast(address) + 0x3F) & ~0x3F); +} + +#if LL_LINUX || LL_DARWIN + +#define LL_ALIGN_PREFIX(x) +#define LL_ALIGN_POSTFIX(x) __attribute__((aligned(x))) + +#elif LL_WINDOWS + +#define LL_ALIGN_PREFIX(x) __declspec(align(x)) +#define LL_ALIGN_POSTFIX(x) + +#else +#error "LL_ALIGN_PREFIX and LL_ALIGN_POSTFIX undefined" +#endif + +#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16) + + + +#include +#include + +#include "llsimdtypes.h" +#include "llsimdtypes.inl" + +class LLMatrix3a; +class LLRotation; +class LLMatrix3; + +#include "llquaternion.h" + +#include "llvector4logical.h" +#include "llvector4a.h" +#include "llmatrix3a.h" +#include "llquaternion2.h" +#include "llvector4a.inl" +#include "llmatrix3a.inl" +#include "llquaternion2.inl" + + +#endif //LL_SIMD_MATH_H diff --git a/indra/llmath/llsimdtypes.h b/indra/llmath/llsimdtypes.h new file mode 100644 index 0000000000..82e318c8bf --- /dev/null +++ b/indra/llmath/llsimdtypes.h @@ -0,0 +1,130 @@ +/** + * @file llsimdtypes.h + * @brief Declaration of basic SIMD math related types + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_SIMD_TYPES_H +#define LL_SIMD_TYPES_H + +#ifndef LL_SIMD_MATH_H +#error "Please include llmath.h before this file." +#endif + +typedef __m128 LLQuad; + + +#if LL_WINDOWS +#pragma warning(push) +#pragma warning( disable : 4800 3 ) // Disable warning about casting int to bool for this class. +#if defined(_MSC_VER) && (_MSC_VER < 1500) +// VC++ 2005 is missing these intrinsics +// __forceinline is MSVC specific and attempts to override compiler inlining judgment. This is so +// even in debug builds this call is a NOP. +__forceinline const __m128 _mm_castsi128_ps( const __m128i a ) { return reinterpret_cast(a); } +__forceinline const __m128i _mm_castps_si128( const __m128 a ) { return reinterpret_cast(a); } +#endif // _MSC_VER + +#endif // LL_WINDOWS + +class LLBool32 +{ +public: + inline LLBool32() {} + inline LLBool32(int rhs) : m_bool(rhs) {} + inline LLBool32(unsigned int rhs) : m_bool(rhs) {} + inline LLBool32(bool rhs) { m_bool = static_cast(rhs); } + inline LLBool32& operator= (bool rhs) { m_bool = (int)rhs; return *this; } + inline bool operator== (bool rhs) const { return static_cast(m_bool) == rhs; } + inline bool operator!= (bool rhs) const { return !operator==(rhs); } + inline operator bool() const { return static_cast(m_bool); } + +private: + int m_bool; +}; + +#if LL_WINDOWS +#pragma warning(pop) +#endif + +class LLSimdScalar +{ +public: + inline LLSimdScalar() {} + inline LLSimdScalar(LLQuad q) + { + mQ = q; + } + + inline LLSimdScalar(F32 f) + { + mQ = _mm_set_ss(f); + } + + static inline const LLSimdScalar& getZero() + { + extern const LLQuad F_ZERO_4A; + return reinterpret_cast(F_ZERO_4A); + } + + inline F32 getF32() const; + + inline LLBool32 isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance = F_APPROXIMATELY_ZERO) const; + + inline LLSimdScalar getAbs() const; + + inline void setMax( const LLSimdScalar& a, const LLSimdScalar& b ); + + inline void setMin( const LLSimdScalar& a, const LLSimdScalar& b ); + + inline LLSimdScalar& operator=(F32 rhs); + + inline LLSimdScalar& operator+=(const LLSimdScalar& rhs); + + inline LLSimdScalar& operator-=(const LLSimdScalar& rhs); + + inline LLSimdScalar& operator*=(const LLSimdScalar& rhs); + + inline LLSimdScalar& operator/=(const LLSimdScalar& rhs); + + inline operator LLQuad() const + { + return mQ; + } + + inline const LLQuad& getQuad() const + { + return mQ; + } + +private: + LLQuad mQ; +}; + +#endif //LL_SIMD_TYPES_H diff --git a/indra/llmath/llsimdtypes.inl b/indra/llmath/llsimdtypes.inl new file mode 100644 index 0000000000..69c858e310 --- /dev/null +++ b/indra/llmath/llsimdtypes.inl @@ -0,0 +1,163 @@ +/** + * @file llsimdtypes.inl + * @brief Inlined definitions of basic SIMD math related types + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + + + +////////////////// +// LLSimdScalar +////////////////// + +inline LLSimdScalar operator+(const LLSimdScalar& a, const LLSimdScalar& b) +{ + LLSimdScalar t(a); + t += b; + return t; +} + +inline LLSimdScalar operator-(const LLSimdScalar& a, const LLSimdScalar& b) +{ + LLSimdScalar t(a); + t -= b; + return t; +} + +inline LLSimdScalar operator*(const LLSimdScalar& a, const LLSimdScalar& b) +{ + LLSimdScalar t(a); + t *= b; + return t; +} + +inline LLSimdScalar operator/(const LLSimdScalar& a, const LLSimdScalar& b) +{ + LLSimdScalar t(a); + t /= b; + return t; +} + +inline LLSimdScalar operator-(const LLSimdScalar& a) +{ + static LL_ALIGN_16(const U32 signMask[4]) = {0x80000000, 0x80000000, 0x80000000, 0x80000000 }; + return _mm_xor_ps(*reinterpret_cast(signMask), a); +} + +inline LLBool32 operator==(const LLSimdScalar& a, const LLSimdScalar& b) +{ + return _mm_comieq_ss(a, b); +} + +inline LLBool32 operator!=(const LLSimdScalar& a, const LLSimdScalar& b) +{ + return _mm_comineq_ss(a, b); +} + +inline LLBool32 operator<(const LLSimdScalar& a, const LLSimdScalar& b) +{ + return _mm_comilt_ss(a, b); +} + +inline LLBool32 operator<=(const LLSimdScalar& a, const LLSimdScalar& b) +{ + return _mm_comile_ss(a, b); +} + +inline LLBool32 operator>(const LLSimdScalar& a, const LLSimdScalar& b) +{ + return _mm_comigt_ss(a, b); +} + +inline LLBool32 operator>=(const LLSimdScalar& a, const LLSimdScalar& b) +{ + return _mm_comige_ss(a, b); +} + +inline LLBool32 LLSimdScalar::isApproximatelyEqual(const LLSimdScalar& rhs, F32 tolerance /* = F_APPROXIMATELY_ZERO */) const +{ + const LLSimdScalar tol( tolerance ); + const LLSimdScalar diff = _mm_sub_ss( mQ, rhs.mQ ); + const LLSimdScalar absDiff = diff.getAbs(); + return absDiff <= tol; +} + +inline void LLSimdScalar::setMax( const LLSimdScalar& a, const LLSimdScalar& b ) +{ + mQ = _mm_max_ss( a, b ); +} + +inline void LLSimdScalar::setMin( const LLSimdScalar& a, const LLSimdScalar& b ) +{ + mQ = _mm_min_ss( a, b ); +} + +inline LLSimdScalar& LLSimdScalar::operator=(F32 rhs) +{ + mQ = _mm_set_ss(rhs); + return *this; +} + +inline LLSimdScalar& LLSimdScalar::operator+=(const LLSimdScalar& rhs) +{ + mQ = _mm_add_ss( mQ, rhs ); + return *this; +} + +inline LLSimdScalar& LLSimdScalar::operator-=(const LLSimdScalar& rhs) +{ + mQ = _mm_sub_ss( mQ, rhs ); + return *this; +} + +inline LLSimdScalar& LLSimdScalar::operator*=(const LLSimdScalar& rhs) +{ + mQ = _mm_mul_ss( mQ, rhs ); + return *this; +} + +inline LLSimdScalar& LLSimdScalar::operator/=(const LLSimdScalar& rhs) +{ + mQ = _mm_div_ss( mQ, rhs ); + return *this; +} + +inline LLSimdScalar LLSimdScalar::getAbs() const +{ + static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; + return _mm_and_ps( mQ, *reinterpret_cast(F_ABS_MASK_4A)); +} + +inline F32 LLSimdScalar::getF32() const +{ + F32 ret; + _mm_store_ss(&ret, mQ); + return ret; +} diff --git a/indra/llmath/llvector4a.cpp b/indra/llmath/llvector4a.cpp new file mode 100644 index 0000000000..b62c17302f --- /dev/null +++ b/indra/llmath/llvector4a.cpp @@ -0,0 +1,228 @@ +/** + * @file llvector4a.cpp + * @brief SIMD vector implementation + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llmath.h" +#include "llquantize.h" + +extern const LLQuad F_ZERO_4A = { 0, 0, 0, 0 }; +extern const LLQuad F_APPROXIMATELY_ZERO_4A = { + F_APPROXIMATELY_ZERO, + F_APPROXIMATELY_ZERO, + F_APPROXIMATELY_ZERO, + F_APPROXIMATELY_ZERO +}; + +extern const LLVector4a LL_V4A_ZERO = reinterpret_cast ( F_ZERO_4A ); +extern const LLVector4a LL_V4A_EPSILON = reinterpret_cast ( F_APPROXIMATELY_ZERO_4A ); + +/*static */void LLVector4a::memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes) +{ + assert(src != NULL); + assert(dst != NULL); + assert(bytes > 0); + assert((bytes % sizeof(F32))== 0); + + F32* end = dst + (bytes / sizeof(F32) ); + + if (bytes > 64) + { + F32* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst); + + //at least 64 (16*4) bytes before the end of the destination, switch to 16 byte copies + F32* end_64 = end-16; + + _mm_prefetch((char*)begin_64, _MM_HINT_NTA); + _mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA); + _mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA); + _mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA); + + while (dst < begin_64) + { + copy4a(dst, src); + dst += 4; + src += 4; + } + + while (dst < end_64) + { + _mm_prefetch((char*)src + 512, _MM_HINT_NTA); + _mm_prefetch((char*)dst + 512, _MM_HINT_NTA); + copy4a(dst, src); + copy4a(dst+4, src+4); + copy4a(dst+8, src+8); + copy4a(dst+12, src+12); + + dst += 16; + src += 16; + } + } + + while (dst < end) + { + copy4a(dst, src); + dst += 4; + src += 4; + } +} + +void LLVector4a::setRotated( const LLRotation& rot, const LLVector4a& vec ) +{ + const LLVector4a col0 = rot.getColumn(0); + const LLVector4a col1 = rot.getColumn(1); + const LLVector4a col2 = rot.getColumn(2); + + LLVector4a result = _mm_load_ss( vec.getF32ptr() ); + result.splat<0>( result ); + result.mul( col0 ); + + { + LLVector4a yyyy = _mm_load_ss( vec.getF32ptr() + 1 ); + yyyy.splat<0>( yyyy ); + yyyy.mul( col1 ); + result.add( yyyy ); + } + + { + LLVector4a zzzz = _mm_load_ss( vec.getF32ptr() + 2 ); + zzzz.splat<0>( zzzz ); + zzzz.mul( col2 ); + result.add( zzzz ); + } + + *this = result; +} + +void LLVector4a::setRotated( const LLQuaternion2& quat, const LLVector4a& vec ) +{ + const LLVector4a& quatVec = quat.getVector4a(); + LLVector4a temp; temp.setCross3(quatVec, vec); + temp.add( temp ); + + const LLVector4a realPart( quatVec.getScalarAt<3>() ); + LLVector4a tempTimesReal; tempTimesReal.setMul( temp, realPart ); + + mQ = vec; + add( tempTimesReal ); + + LLVector4a imagCrossTemp; imagCrossTemp.setCross3( quatVec, temp ); + add(imagCrossTemp); +} + +void LLVector4a::quantize8( const LLVector4a& low, const LLVector4a& high ) +{ + LLVector4a val(mQ); + LLVector4a delta; delta.setSub( high, low ); + + { + val.clamp(low, high); + val.sub(low); + + // 8-bit quantization means we can do with just 12 bits of reciprocal accuracy + const LLVector4a oneOverDelta = _mm_rcp_ps(delta.mQ); +// { +// static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; +// LLVector4a two; two.load4a( F_TWO_4A ); +// +// // Here we use _mm_rcp_ps plus one round of newton-raphson +// // We wish to find 'x' such that x = 1/delta +// // As a first approximation, we take x0 = _mm_rcp_ps(delta) +// // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) +// // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf +// const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); +// oneOverDelta.setMul( delta, recipApprox ); +// oneOverDelta.setSub( two, oneOverDelta ); +// oneOverDelta.mul( recipApprox ); +// } + + val.mul(oneOverDelta); + val.mul(*reinterpret_cast(F_U8MAX_4A)); + } + + val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); + + { + val.mul(*reinterpret_cast(F_OOU8MAX_4A)); + val.mul(delta); + val.add(low); + } + + { + LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast(F_OOU8MAX_4A)); + LLVector4a absVal; absVal.setAbs( val ); + setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); + } +} + +void LLVector4a::quantize16( const LLVector4a& low, const LLVector4a& high ) +{ + LLVector4a val(mQ); + LLVector4a delta; delta.setSub( high, low ); + + { + val.clamp(low, high); + val.sub(low); + + // 16-bit quantization means we need a round of Newton-Raphson + LLVector4a oneOverDelta; + { + static LL_ALIGN_16( const F32 F_TWO_4A[4] ) = { 2.f, 2.f, 2.f, 2.f }; + LLVector4a two; two.load4a( F_TWO_4A ); + + // Here we use _mm_rcp_ps plus one round of newton-raphson + // We wish to find 'x' such that x = 1/delta + // As a first approximation, we take x0 = _mm_rcp_ps(delta) + // Then x1 = 2 * x0 - a * x0^2 or x1 = x0 * ( 2 - a * x0 ) + // See Intel AP-803 http://ompf.org/!/Intel_application_note_AP-803.pdf + const LLVector4a recipApprox = _mm_rcp_ps(delta.mQ); + oneOverDelta.setMul( delta, recipApprox ); + oneOverDelta.setSub( two, oneOverDelta ); + oneOverDelta.mul( recipApprox ); + } + + val.mul(oneOverDelta); + val.mul(*reinterpret_cast(F_U16MAX_4A)); + } + + val = _mm_cvtepi32_ps(_mm_cvtps_epi32( val.mQ )); + + { + val.mul(*reinterpret_cast(F_OOU16MAX_4A)); + val.mul(delta); + val.add(low); + } + + { + LLVector4a maxError; maxError.setMul(delta, *reinterpret_cast(F_OOU16MAX_4A)); + LLVector4a absVal; absVal.setAbs( val ); + setSelectWithMask( absVal.lessThan( maxError ), F_ZERO_4A, val ); + } +} diff --git a/indra/llmath/llvector4a.h b/indra/llmath/llvector4a.h new file mode 100644 index 0000000000..76a3e999ce --- /dev/null +++ b/indra/llmath/llvector4a.h @@ -0,0 +1,331 @@ +/** + * @file llvector4a.h + * @brief LLVector4a class header file - memory aligned and vectorized 4 component vector + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLVECTOR4A_H +#define LL_LLVECTOR4A_H + + +class LLRotation; + +#include +#include "llpreprocessor.h" + +/////////////////////////////////// +// FIRST TIME USERS PLEASE READ +////////////////////////////////// +// This is just the beginning of LLVector4a. There are many more useful functions +// yet to be implemented. For example, setNeg to negate a vector, rotate() to apply +// a matrix rotation, various functions to manipulate only the X, Y, and Z elements +// and many others (including a whole variety of accessors). So if you don't see a +// function here that you need, please contact Falcon or someone else with SSE +// experience (Richard, I think, has some and davep has a little as of the time +// of this writing, July 08, 2010) about getting it implemented before you resort to +// LLVector3/LLVector4. +///////////////////////////////// + +class LLVector4a +{ +public: + + /////////////////////////////////// + // STATIC METHODS + /////////////////////////////////// + + // Call initClass() at startup to avoid 15,000+ cycle penalties from denormalized numbers + static void initClass() + { + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_ROUNDING_MODE(_MM_ROUND_NEAREST); + } + + // Return a vector of all zeros + static inline const LLVector4a& getZero() + { + extern const LLVector4a LL_V4A_ZERO; + return LL_V4A_ZERO; + } + + // Return a vector of all epsilon, where epsilon is a small float suitable for approximate equality checks + static inline const LLVector4a& getEpsilon() + { + extern const LLVector4a LL_V4A_EPSILON; + return LL_V4A_EPSILON; + } + + // Copy 16 bytes from src to dst. Source and destination must be 16-byte aligned + static inline void copy4a(F32* dst, const F32* src) + { + _mm_store_ps(dst, _mm_load_ps(src)); + } + + // Copy words 16-byte blocks from src to dst. Source and destination must not overlap. + static void memcpyNonAliased16(F32* __restrict dst, const F32* __restrict src, size_t bytes); + + //////////////////////////////////// + // CONSTRUCTORS + //////////////////////////////////// + + LLVector4a() + { //DO NOT INITIALIZE -- The overhead is completely unnecessary + } + + LLVector4a(F32 x, F32 y, F32 z, F32 w = 0.f) + { + set(x,y,z,w); + } + + LLVector4a(F32 x) + { + splat(x); + } + + LLVector4a(const LLSimdScalar& x) + { + splat(x); + } + + LLVector4a(LLQuad q) + { + mQ = q; + } + + //////////////////////////////////// + // LOAD/STORE + //////////////////////////////////// + + // Load from 16-byte aligned src array (preferred method of loading) + inline void load4a(const F32* src); + + // Load from unaligned src array (NB: Significantly slower than load4a) + inline void loadua(const F32* src); + + // Load only three floats beginning at address 'src'. Slowest method. + inline void load3(const F32* src); + + // Store to a 16-byte aligned memory address + inline void store4a(F32* dst) const; + + //////////////////////////////////// + // BASIC GET/SET + //////////////////////////////////// + + // Return a "this" as an F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon) + inline F32* getF32ptr(); + + // Return a "this" as a const F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon) + inline const F32* const getF32ptr() const; + + // Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates + // the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead + inline F32 operator[](const S32 idx) const; + + // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. + inline LLSimdScalar getScalarAt(const S32 idx) const; + + // Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. + template LL_FORCE_INLINE LLSimdScalar getScalarAt() const; + template <> LL_FORCE_INLINE LLSimdScalar getScalarAt<0>() const; + + // Set to an x, y, z and optional w provided + inline void set(F32 x, F32 y, F32 z, F32 w = 0.f); + + // Set to all zeros. This is preferred to using ::getZero() + inline void clear(); + + // Set all elements to 'x' + inline void splat(const F32 x); + + // Set all elements to 'x' + inline void splat(const LLSimdScalar& x); + + // Set all 4 elements to element N of src, with N known at compile time + template void splat(const LLVector4a& src); + + // Set all 4 elements to element i of v, with i NOT known at compile time + inline void splat(const LLVector4a& v, U32 i); + + // Select bits from sourceIfTrue and sourceIfFalse according to bits in mask + inline void setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse ); + + //////////////////////////////////// + // ALGEBRAIC + //////////////////////////////////// + + // Set this to the element-wise (a + b) + inline void setAdd(const LLVector4a& a, const LLVector4a& b); + + // Set this to element-wise (a - b) + inline void setSub(const LLVector4a& a, const LLVector4a& b); + + // Set this to element-wise multiply (a * b) + inline void setMul(const LLVector4a& a, const LLVector4a& b); + + // Set this to element-wise quotient (a / b) + inline void setDiv(const LLVector4a& a, const LLVector4a& b); + + // Set this to the element-wise absolute value of src + inline void setAbs(const LLVector4a& src); + + // Add to each component in this vector the corresponding component in rhs + inline void add(const LLVector4a& rhs); + + // Subtract from each component in this vector the corresponding component in rhs + inline void sub(const LLVector4a& rhs); + + // Multiply each component in this vector by the corresponding component in rhs + inline void mul(const LLVector4a& rhs); + + // Divide each component in this vector by the corresponding component in rhs + inline void div(const LLVector4a& rhs); + + // Multiply this vector by x in a scalar fashion + inline void mul(const F32 x); + + // Set this to (a x b) (geometric cross-product) + inline void setCross3(const LLVector4a& a, const LLVector4a& b); + + // Set all elements to the dot product of the x, y, and z elements in a and b + inline void setAllDot3(const LLVector4a& a, const LLVector4a& b); + + // Set all elements to the dot product of the x, y, z, and w elements in a and b + inline void setAllDot4(const LLVector4a& a, const LLVector4a& b); + + // Return the 3D dot product of this vector and b + inline LLSimdScalar dot3(const LLVector4a& b) const; + + // Return the 4D dot product of this vector and b + inline LLSimdScalar dot4(const LLVector4a& b) const; + + // Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed + // Note that this does not consider zero length vectors! + inline void normalize3(); + + // Same as normalize3() but with respect to all 4 components + inline void normalize4(); + + // Same as normalize3(), but returns length as a SIMD scalar + inline LLSimdScalar normalize3withLength(); + + // Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed + // Note that this does not consider zero length vectors! + inline void normalize3fast(); + + // Return true if this vector is normalized with respect to x,y,z up to tolerance + inline LLBool32 isNormalized3( F32 tolerance = 1e-3 ) const; + + // Return true if this vector is normalized with respect to all components up to tolerance + inline LLBool32 isNormalized4( F32 tolerance = 1e-3 ) const; + + // Set all elements to the length of vector 'v' + inline void setAllLength3( const LLVector4a& v ); + + // Get this vector's length + inline LLSimdScalar getLength3() const; + + // Set the components of this vector to the minimum of the corresponding components of lhs and rhs + inline void setMin(const LLVector4a& lhs, const LLVector4a& rhs); + + // Set the components of this vector to the maximum of the corresponding components of lhs and rhs + inline void setMax(const LLVector4a& lhs, const LLVector4a& rhs); + + // Clamps this vector to be within the component-wise range low to high (inclusive) + inline void clamp( const LLVector4a& low, const LLVector4a& high ); + + // Set this to (c * lhs) + rhs * ( 1 - c) + inline void setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c); + + // Return true (nonzero) if x, y, z (and w for Finite4) are all finite floats + inline LLBool32 isFinite3() const; + inline LLBool32 isFinite4() const; + + // Set this vector to 'vec' rotated by the LLRotation or LLQuaternion2 provided + void setRotated( const LLRotation& rot, const LLVector4a& vec ); + void setRotated( const class LLQuaternion2& quat, const LLVector4a& vec ); + + // Set this vector to 'vec' rotated by the INVERSE of the LLRotation or LLQuaternion2 provided + inline void setRotatedInv( const LLRotation& rot, const LLVector4a& vec ); + inline void setRotatedInv( const class LLQuaternion2& quat, const LLVector4a& vec ); + + // Quantize this vector to 8 or 16 bit precision + void quantize8( const LLVector4a& low, const LLVector4a& high ); + void quantize16( const LLVector4a& low, const LLVector4a& high ); + + //////////////////////////////////// + // LOGICAL + //////////////////////////////////// + // The functions in this section will compare the elements in this vector + // to those in rhs and return an LLVector4Logical with all bits set in elements + // where the comparison was true and all bits unset in elements where the comparison + // was false. See llvector4logica.h + //////////////////////////////////// + // WARNING: Other than equals3 and equals4, these functions do NOT account + // for floating point tolerance. You should include the appropriate tolerance + // in the inputs. + //////////////////////////////////// + + inline LLVector4Logical greaterThan(const LLVector4a& rhs) const; + + inline LLVector4Logical lessThan(const LLVector4a& rhs) const; + + inline LLVector4Logical greaterEqual(const LLVector4a& rhs) const; + + inline LLVector4Logical lessEqual(const LLVector4a& rhs) const; + + inline LLVector4Logical equal(const LLVector4a& rhs) const; + + // Returns true if this and rhs are componentwise equal up to the specified absolute tolerance + inline bool equals4(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + + inline bool equals3(const LLVector4a& rhs, F32 tolerance = F_APPROXIMATELY_ZERO ) const; + + //////////////////////////////////// + // OPERATORS + //////////////////////////////////// + + // Do NOT add aditional operators without consulting someone with SSE experience + inline const LLVector4a& operator= ( const LLVector4a& rhs ); + + inline const LLVector4a& operator= ( const LLQuad& rhs ); + + inline operator LLQuad() const; + +private: + LLQuad mQ; +}; + +inline void update_min_max(LLVector4a& min, LLVector4a& max, const LLVector4a& p) +{ + min.setMin(min, p); + max.setMax(max, p); +} + +#endif diff --git a/indra/llmath/llvector4a.inl b/indra/llmath/llvector4a.inl new file mode 100644 index 0000000000..e52b550883 --- /dev/null +++ b/indra/llmath/llvector4a.inl @@ -0,0 +1,599 @@ +/** + * @file llvector4a.inl + * @brief LLVector4a inline function implementations + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +//////////////////////////////////// +// LOAD/STORE +//////////////////////////////////// + +// Load from 16-byte aligned src array (preferred method of loading) +inline void LLVector4a::load4a(const F32* src) +{ + mQ = _mm_load_ps(src); +} + +// Load from unaligned src array (NB: Significantly slower than load4a) +inline void LLVector4a::loadua(const F32* src) +{ + mQ = _mm_loadu_ps(src); +} + +// Load only three floats beginning at address 'src'. Slowest method. +inline void LLVector4a::load3(const F32* src) +{ + // mQ = { 0.f, src[2], src[1], src[0] } = { W, Z, Y, X } + // NB: This differs from the convention of { Z, Y, X, W } + mQ = _mm_set_ps(0.f, src[2], src[1], src[0]); +} + +// Store to a 16-byte aligned memory address +inline void LLVector4a::store4a(F32* dst) const +{ + _mm_store_ps(dst, mQ); +} + +//////////////////////////////////// +// BASIC GET/SET +//////////////////////////////////// + +// Return a "this" as an F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon) +F32* LLVector4a::getF32ptr() +{ + return (F32*) &mQ; +} + +// Return a "this" as a const F32 pointer. Do not use unless you have a very good reason. (Not sure? Ask Falcon) +const F32* const LLVector4a::getF32ptr() const +{ + return (const F32* const) &mQ; +} + +// Read-only access a single float in this vector. Do not use in proximity to any function call that manipulates +// the data at the whole vector level or you will incur a substantial penalty. Consider using the splat functions instead +inline F32 LLVector4a::operator[](const S32 idx) const +{ + return ((F32*)&mQ)[idx]; +} + +// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. +inline LLSimdScalar LLVector4a::getScalarAt(const S32 idx) const +{ + // Return appropriate LLQuad. It will be cast to LLSimdScalar automatically (should be effectively a nop) + switch (idx) + { + case 0: + return mQ; + case 1: + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(1, 1, 1, 1)); + case 2: + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(2, 2, 2, 2)); + case 3: + default: + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(3, 3, 3, 3)); + } +} + +// Prefer this method for read-only access to a single element. Prefer the templated version if the elem is known at compile time. +template LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt() const +{ + return _mm_shuffle_ps(mQ, mQ, _MM_SHUFFLE(N, N, N, N)); +} + +template<> LL_FORCE_INLINE LLSimdScalar LLVector4a::getScalarAt<0>() const +{ + return mQ; +} + +// Set to an x, y, z and optional w provided +inline void LLVector4a::set(F32 x, F32 y, F32 z, F32 w) +{ + mQ = _mm_set_ps(w, z, y, x); +} + +// Set to all zeros +inline void LLVector4a::clear() +{ + mQ = LLVector4a::getZero().mQ; +} + +inline void LLVector4a::splat(const F32 x) +{ + mQ = _mm_set1_ps(x); +} + +inline void LLVector4a::splat(const LLSimdScalar& x) +{ + mQ = _mm_shuffle_ps( x.getQuad(), x.getQuad(), _MM_SHUFFLE(0,0,0,0) ); +} + +// Set all 4 elements to element N of src, with N known at compile time +template void LLVector4a::splat(const LLVector4a& src) +{ + mQ = _mm_shuffle_ps(src.mQ, src.mQ, _MM_SHUFFLE(N, N, N, N) ); +} + +// Set all 4 elements to element i of v, with i NOT known at compile time +inline void LLVector4a::splat(const LLVector4a& v, U32 i) +{ + switch (i) + { + case 0: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(0, 0, 0, 0)); + break; + case 1: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(1, 1, 1, 1)); + break; + case 2: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(2, 2, 2, 2)); + break; + case 3: + mQ = _mm_shuffle_ps(v.mQ, v.mQ, _MM_SHUFFLE(3, 3, 3, 3)); + break; + } +} + +// Select bits from sourceIfTrue and sourceIfFalse according to bits in mask +inline void LLVector4a::setSelectWithMask( const LLVector4Logical& mask, const LLVector4a& sourceIfTrue, const LLVector4a& sourceIfFalse ) +{ + // ((( sourceIfTrue ^ sourceIfFalse ) & mask) ^ sourceIfFalse ) + // E.g., sourceIfFalse = 1010b, sourceIfTrue = 0101b, mask = 1100b + // (sourceIfTrue ^ sourceIfFalse) = 1111b --> & mask = 1100b --> ^ sourceIfFalse = 0110b, + // as expected (01 from sourceIfTrue, 10 from sourceIfFalse) + // Courtesy of Mark++, http://markplusplus.wordpress.com/2007/03/14/fast-sse-select-operation/ + mQ = _mm_xor_ps( sourceIfFalse, _mm_and_ps( mask, _mm_xor_ps( sourceIfTrue, sourceIfFalse ) ) ); +} + +//////////////////////////////////// +// ALGEBRAIC +//////////////////////////////////// + +// Set this to the element-wise (a + b) +inline void LLVector4a::setAdd(const LLVector4a& a, const LLVector4a& b) +{ + mQ = _mm_add_ps(a.mQ, b.mQ); +} + +// Set this to element-wise (a - b) +inline void LLVector4a::setSub(const LLVector4a& a, const LLVector4a& b) +{ + mQ = _mm_sub_ps(a.mQ, b.mQ); +} + +// Set this to element-wise multiply (a * b) +inline void LLVector4a::setMul(const LLVector4a& a, const LLVector4a& b) +{ + mQ = _mm_mul_ps(a.mQ, b.mQ); +} + +// Set this to element-wise quotient (a / b) +inline void LLVector4a::setDiv(const LLVector4a& a, const LLVector4a& b) +{ + mQ = _mm_div_ps( a.mQ, b.mQ ); +} + +// Set this to the element-wise absolute value of src +inline void LLVector4a::setAbs(const LLVector4a& src) +{ + static const LL_ALIGN_16(U32 F_ABS_MASK_4A[4]) = { 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF }; + mQ = _mm_and_ps(src.mQ, *reinterpret_cast(F_ABS_MASK_4A)); +} + +// Add to each component in this vector the corresponding component in rhs +inline void LLVector4a::add(const LLVector4a& rhs) +{ + mQ = _mm_add_ps(mQ, rhs.mQ); +} + +// Subtract from each component in this vector the corresponding component in rhs +inline void LLVector4a::sub(const LLVector4a& rhs) +{ + mQ = _mm_sub_ps(mQ, rhs.mQ); +} + +// Multiply each component in this vector by the corresponding component in rhs +inline void LLVector4a::mul(const LLVector4a& rhs) +{ + mQ = _mm_mul_ps(mQ, rhs.mQ); +} + +// Divide each component in this vector by the corresponding component in rhs +inline void LLVector4a::div(const LLVector4a& rhs) +{ + // TODO: Check accuracy, maybe add divFast + mQ = _mm_div_ps(mQ, rhs.mQ); +} + +// Multiply this vector by x in a scalar fashion +inline void LLVector4a::mul(const F32 x) +{ + LLVector4a t; + t.splat(x); + + mQ = _mm_mul_ps(mQ, t.mQ); +} + +// Set this to (a x b) (geometric cross-product) +inline void LLVector4a::setCross3(const LLVector4a& a, const LLVector4a& b) +{ + // Vectors are stored in memory in w, z, y, x order from high to low + // Set vector1 = { a[W], a[X], a[Z], a[Y] } + const LLQuad vector1 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 0, 2, 1 )); + // Set vector2 = { b[W], b[Y], b[X], b[Z] } + const LLQuad vector2 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 1, 0, 2 )); + // mQ = { a[W]*b[W], a[X]*b[Y], a[Z]*b[X], a[Y]*b[Z] } + mQ = _mm_mul_ps( vector1, vector2 ); + // vector3 = { a[W], a[Y], a[X], a[Z] } + const LLQuad vector3 = _mm_shuffle_ps( a.mQ, a.mQ, _MM_SHUFFLE( 3, 1, 0, 2 )); + // vector4 = { b[W], b[X], b[Z], b[Y] } + const LLQuad vector4 = _mm_shuffle_ps( b.mQ, b.mQ, _MM_SHUFFLE( 3, 0, 2, 1 )); + // mQ = { 0, a[X]*b[Y] - a[Y]*b[X], a[Z]*b[X] - a[X]*b[Z], a[Y]*b[Z] - a[Z]*b[Y] } + mQ = _mm_sub_ps( mQ, _mm_mul_ps( vector3, vector4 )); +} + +/* This function works, but may be slightly slower than the one below on older machines + inline void LLVector4a::setAllDot3(const LLVector4a& a, const LLVector4a& b) + { + // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] } + const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ ); + // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] } + const LLQuad wzxy = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE(3, 2, 0, 1 )); + // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusY = _mm_add_ps(ab, wzxy); + // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY); + // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] } + const LLQuad zSplat = _mm_shuffle_ps( ab, ab, _MM_SHUFFLE( 2, 2, 2, 2 )); + // mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same } + mQ = _mm_add_ps(zSplat, xPlusYSplat); + }*/ + +// Set all elements to the dot product of the x, y, and z elements in a and b +inline void LLVector4a::setAllDot3(const LLVector4a& a, const LLVector4a& b) +{ + // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] } + const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ ); + // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] } + const __m128i wzxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(3, 2, 0, 1 )); + // xPlusY = { 2*a[W]*b[W], 2 * a[Z] * b[Z], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusY = _mm_add_ps(ab, _mm_castsi128_ps(wzxy)); + // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusYSplat = _mm_movelh_ps(xPlusY, xPlusY); + // zSplat = { a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z], a[Z]*b[Z] } + const __m128i zSplat = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE( 2, 2, 2, 2 )); + // mQ = { a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same } + mQ = _mm_add_ps(_mm_castsi128_ps(zSplat), xPlusYSplat); +} + +// Set all elements to the dot product of the x, y, z, and w elements in a and b +inline void LLVector4a::setAllDot4(const LLVector4a& a, const LLVector4a& b) +{ + // ab = { a[W]*b[W], a[Z]*b[Z], a[Y]*b[Y], a[X]*b[X] } + const LLQuad ab = _mm_mul_ps( a.mQ, b.mQ ); + // yzxw = { a[W]*b[W], a[Z]*b[Z], a[X]*b[X], a[Y]*b[Y] } + const __m128i zwxy = _mm_shuffle_epi32(_mm_castps_si128(ab), _MM_SHUFFLE(2, 3, 0, 1 )); + // zPlusWandXplusY = { a[W]*b[W] + a[Z]*b[Z], a[Z] * b[Z] + a[W]*b[W], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad zPlusWandXplusY = _mm_add_ps(ab, _mm_castsi128_ps(zwxy)); + // xPlusYSplat = { a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y], a[Y]*b[Y] + a[X] * b[X], a[X] * b[X] + a[Y] * b[Y] } + const LLQuad xPlusYSplat = _mm_movelh_ps(zPlusWandXplusY, zPlusWandXplusY); + const LLQuad zPlusWSplat = _mm_movehl_ps(zPlusWandXplusY, zPlusWandXplusY); + + // mQ = { a[W]*b[W] + a[Z] * b[Z] + a[Y] * b[Y] + a[X] * b[X], same, same, same } + mQ = _mm_add_ps(xPlusYSplat, zPlusWSplat); +} + +// Return the 3D dot product of this vector and b +inline LLSimdScalar LLVector4a::dot3(const LLVector4a& b) const +{ + const LLQuad ab = _mm_mul_ps( mQ, b.mQ ); + const LLQuad splatY = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(1, 1, 1, 1) ) ); + const LLQuad splatZ = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128(ab), _MM_SHUFFLE(2, 2, 2, 2) ) ); + const LLQuad xPlusY = _mm_add_ps( ab, splatY ); + return _mm_add_ps( xPlusY, splatZ ); +} + +// Return the 4D dot product of this vector and b +inline LLSimdScalar LLVector4a::dot4(const LLVector4a& b) const +{ + // ab = { w, z, y, x } + const LLQuad ab = _mm_mul_ps( mQ, b.mQ ); + // upperProdsInLowerElems = { y, x, y, x } + const LLQuad upperProdsInLowerElems = _mm_movehl_ps( ab, ab ); + // sumOfPairs = { w+y, z+x, 2y, 2x } + const LLQuad sumOfPairs = _mm_add_ps( upperProdsInLowerElems, ab ); + // shuffled = { z+x, z+x, z+x, z+x } + const LLQuad shuffled = _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( sumOfPairs ), _MM_SHUFFLE(1, 1, 1, 1) ) ); + return _mm_add_ss( sumOfPairs, shuffled ); +} + +// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed +// Note that this does not consider zero length vectors! +inline void LLVector4a::normalize3() +{ + // lenSqrd = a dot a + LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); + // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } + const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; + static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; + // Now we do one round of Newton-Raphson approximation to get full accuracy + // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) + // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) + // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 + // = 0.5 * w * (3 - a*w^2) + // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula + // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] + const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); + const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); + const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); + const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); + mQ = _mm_mul_ps( mQ, nrApprox ); +} + +// Normalize this vector with respect to all components. Accurate to 22 bites of precision. +// Note that this does not consider zero length vectors! +inline void LLVector4a::normalize4() +{ + // lenSqrd = a dot a + LLVector4a lenSqrd; lenSqrd.setAllDot4( *this, *this ); + // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } + const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; + static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; + // Now we do one round of Newton-Raphson approximation to get full accuracy + // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) + // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) + // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 + // = 0.5 * w * (3 - a*w^2) + // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula + // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] + const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); + const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); + const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); + const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); + mQ = _mm_mul_ps( mQ, nrApprox ); +} + +// Normalize this vector with respect to the x, y, and z components only. Accurate to 22 bites of precision. W component is destroyed +// Note that this does not consider zero length vectors! +inline LLSimdScalar LLVector4a::normalize3withLength() +{ + // lenSqrd = a dot a + LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); + // rsqrt = approximate reciprocal square (i.e., { ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2, ~1/len(a)^2 } + const LLQuad rsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + static const LLQuad half = { 0.5f, 0.5f, 0.5f, 0.5f }; + static const LLQuad three = {3.f, 3.f, 3.f, 3.f }; + // Now we do one round of Newton-Raphson approximation to get full accuracy + // According to the Newton-Raphson method, given a first 'w' for the root of f(x) = 1/x^2 - a (i.e., x = 1/sqrt(a)) + // the next better approximation w[i+1] = w - f(w)/f'(w) = w - (1/w^2 - a)/(-2*w^(-3)) + // w[i+1] = w + 0.5 * (1/w^2 - a) * w^3 = w + 0.5 * (w - a*w^3) = 1.5 * w - 0.5 * a * w^3 + // = 0.5 * w * (3 - a*w^2) + // Our first approx is w = rsqrt. We need out = a * w[i+1] (this is the input vector 'a', not the 'a' from the above formula + // which is actually lenSqrd). So out = a * [0.5*rsqrt * (3 - lenSqrd*rsqrt*rsqrt)] + const LLQuad AtimesRsqrt = _mm_mul_ps( lenSqrd.mQ, rsqrt ); + const LLQuad AtimesRsqrtTimesRsqrt = _mm_mul_ps( AtimesRsqrt, rsqrt ); + const LLQuad threeMinusAtimesRsqrtTimesRsqrt = _mm_sub_ps(three, AtimesRsqrtTimesRsqrt ); + const LLQuad nrApprox = _mm_mul_ps(half, _mm_mul_ps(rsqrt, threeMinusAtimesRsqrtTimesRsqrt)); + mQ = _mm_mul_ps( mQ, nrApprox ); + return _mm_sqrt_ss(lenSqrd); +} + +// Normalize this vector with respect to the x, y, and z components only. Accurate only to 10-12 bits of precision. W component is destroyed +// Note that this does not consider zero length vectors! +inline void LLVector4a::normalize3fast() +{ + LLVector4a lenSqrd; lenSqrd.setAllDot3( *this, *this ); + const LLQuad approxRsqrt = _mm_rsqrt_ps(lenSqrd.mQ); + mQ = _mm_mul_ps( mQ, approxRsqrt ); +} + +// Return true if this vector is normalized with respect to x,y,z up to tolerance +inline LLBool32 LLVector4a::isNormalized3( F32 tolerance ) const +{ + static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 }; + LLSimdScalar tol = _mm_load_ss( &tolerance ); + tol = _mm_mul_ss( tol, tol ); + LLVector4a lenSquared; lenSquared.setAllDot3( *this, *this ); + lenSquared.sub( *reinterpret_cast(ones) ); + lenSquared.setAbs(lenSquared); + return _mm_comile_ss( lenSquared, tol ); +} + +// Return true if this vector is normalized with respect to all components up to tolerance +inline LLBool32 LLVector4a::isNormalized4( F32 tolerance ) const +{ + static LL_ALIGN_16(const U32 ones[4]) = { 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 }; + LLSimdScalar tol = _mm_load_ss( &tolerance ); + tol = _mm_mul_ss( tol, tol ); + LLVector4a lenSquared; lenSquared.setAllDot4( *this, *this ); + lenSquared.sub( *reinterpret_cast(ones) ); + lenSquared.setAbs(lenSquared); + return _mm_comile_ss( lenSquared, tol ); +} + +// Set all elements to the length of vector 'v' +inline void LLVector4a::setAllLength3( const LLVector4a& v ) +{ + LLVector4a lenSqrd; + lenSqrd.setAllDot3(v, v); + + mQ = _mm_sqrt_ps(lenSqrd.mQ); +} + +// Get this vector's length +inline LLSimdScalar LLVector4a::getLength3() const +{ + return _mm_sqrt_ss( dot3( (const LLVector4a)mQ ) ); +} + +// Set the components of this vector to the minimum of the corresponding components of lhs and rhs +inline void LLVector4a::setMin(const LLVector4a& lhs, const LLVector4a& rhs) +{ + mQ = _mm_min_ps(lhs.mQ, rhs.mQ); +} + +// Set the components of this vector to the maximum of the corresponding components of lhs and rhs +inline void LLVector4a::setMax(const LLVector4a& lhs, const LLVector4a& rhs) +{ + mQ = _mm_max_ps(lhs.mQ, rhs.mQ); +} + +// Set this to (c * lhs) + rhs * ( 1 - c) +inline void LLVector4a::setLerp(const LLVector4a& lhs, const LLVector4a& rhs, F32 c) +{ + LLVector4a a = lhs; + a.mul(c); + + LLVector4a b = rhs; + b.mul(1.f-c); + + setAdd(a, b); +} + +inline LLBool32 LLVector4a::isFinite3() const +{ + static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; + const __m128i nanOrInfMaskV = *reinterpret_cast (nanOrInfMask); + const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV ); + const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV )); + return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZ ); +} + +inline LLBool32 LLVector4a::isFinite4() const +{ + static LL_ALIGN_16(const U32 nanOrInfMask[4]) = { 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 }; + const __m128i nanOrInfMaskV = *reinterpret_cast (nanOrInfMask); + const __m128i maskResult = _mm_and_si128( _mm_castps_si128(mQ), nanOrInfMaskV ); + const LLVector4Logical equalityCheck = _mm_castsi128_ps(_mm_cmpeq_epi32( maskResult, nanOrInfMaskV )); + return !equalityCheck.areAnySet( LLVector4Logical::MASK_XYZW ); +} + +inline void LLVector4a::setRotatedInv( const LLRotation& rot, const LLVector4a& vec ) +{ + LLRotation inv; inv.setTranspose( rot ); + setRotated( inv, vec ); +} + +inline void LLVector4a::setRotatedInv( const LLQuaternion2& quat, const LLVector4a& vec ) +{ + LLQuaternion2 invRot; invRot.setConjugate( quat ); + setRotated(invRot, vec); +} + +inline void LLVector4a::clamp( const LLVector4a& low, const LLVector4a& high ) +{ + const LLVector4Logical highMask = greaterThan( high ); + const LLVector4Logical lowMask = lessThan( low ); + + setSelectWithMask( highMask, high, *this ); + setSelectWithMask( lowMask, low, *this ); +} + + +//////////////////////////////////// +// LOGICAL +//////////////////////////////////// +// The functions in this section will compare the elements in this vector +// to those in rhs and return an LLVector4Logical with all bits set in elements +// where the comparison was true and all bits unset in elements where the comparison +// was false. See llvector4logica.h +//////////////////////////////////// +// WARNING: Other than equals3 and equals4, these functions do NOT account +// for floating point tolerance. You should include the appropriate tolerance +// in the inputs. +//////////////////////////////////// + +inline LLVector4Logical LLVector4a::greaterThan(const LLVector4a& rhs) const +{ + return _mm_cmpgt_ps(mQ, rhs.mQ); +} + +inline LLVector4Logical LLVector4a::lessThan(const LLVector4a& rhs) const +{ + return _mm_cmplt_ps(mQ, rhs.mQ); +} + +inline LLVector4Logical LLVector4a::greaterEqual(const LLVector4a& rhs) const +{ + return _mm_cmpge_ps(mQ, rhs.mQ); +} + +inline LLVector4Logical LLVector4a::lessEqual(const LLVector4a& rhs) const +{ + return _mm_cmple_ps(mQ, rhs.mQ); +} + +inline LLVector4Logical LLVector4a::equal(const LLVector4a& rhs) const +{ + return _mm_cmpeq_ps(mQ, rhs.mQ); +} + +// Returns true if this and rhs are componentwise equal up to the specified absolute tolerance +inline bool LLVector4a::equals4(const LLVector4a& rhs, F32 tolerance ) const +{ + LLVector4a diff; diff.setSub( *this, rhs ); + diff.setAbs( diff ); + const LLQuad tol = _mm_set1_ps( tolerance ); + const LLQuad cmp = _mm_cmplt_ps( diff, tol ); + return (_mm_movemask_ps( cmp ) & LLVector4Logical::MASK_XYZW) == LLVector4Logical::MASK_XYZW; +} + +inline bool LLVector4a::equals3(const LLVector4a& rhs, F32 tolerance ) const +{ + LLVector4a diff; diff.setSub( *this, rhs ); + diff.setAbs( diff ); + const LLQuad tol = _mm_set1_ps( tolerance ); + const LLQuad t = _mm_cmplt_ps( diff, tol ); + return (_mm_movemask_ps( t ) & LLVector4Logical::MASK_XYZ) == LLVector4Logical::MASK_XYZ; + +} + +//////////////////////////////////// +// OPERATORS +//////////////////////////////////// + +// Do NOT add aditional operators without consulting someone with SSE experience +inline const LLVector4a& LLVector4a::operator= ( const LLVector4a& rhs ) +{ + mQ = rhs.mQ; + return *this; +} + +inline const LLVector4a& LLVector4a::operator= ( const LLQuad& rhs ) +{ + mQ = rhs; + return *this; +} + +inline LLVector4a::operator LLQuad() const +{ + return mQ; +} diff --git a/indra/llmath/llvector4logical.h b/indra/llmath/llvector4logical.h new file mode 100644 index 0000000000..1c7ee1d79f --- /dev/null +++ b/indra/llmath/llvector4logical.h @@ -0,0 +1,130 @@ +/** + * @file llvector4logical.h + * @brief LLVector4Logical class header file - Companion class to LLVector4a for logical and bit-twiddling operations + * + * $LicenseInfo:firstyear=2010&license=viewergpl$ + * + * Copyright (c) 2007-2010, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_VECTOR4LOGICAL_H +#define LL_VECTOR4LOGICAL_H + + +//////////////////////////// +// LLVector4Logical +//////////////////////////// +// This class is incomplete. If you need additional functionality, +// for example setting/unsetting particular elements or performing +// other boolean operations, feel free to implement. If you need +// assistance in determining the most optimal implementation, +// contact someone with SSE experience (Falcon, Richard, Davep, e.g.) +//////////////////////////// + +static LL_ALIGN_16(const U32 S_V4LOGICAL_MASK_TABLE[4*4]) = +{ + 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF +}; + +class LLVector4Logical +{ +public: + + enum { + MASK_X = 1, + MASK_Y = 1 << 1, + MASK_Z = 1 << 2, + MASK_W = 1 << 3, + MASK_XYZ = MASK_X | MASK_Y | MASK_Z, + MASK_XYZW = MASK_XYZ | MASK_W + }; + + // Empty default ctor + LLVector4Logical() {} + + LLVector4Logical( const LLQuad& quad ) + { + mQ = quad; + } + + // Create and return a mask consisting of the lowest order bit of each element + inline U32 getGatheredBits() const + { + return _mm_movemask_ps(mQ); + }; + + // Invert this mask + inline LLVector4Logical& invert() + { + static const LL_ALIGN_16(U32 allOnes[4]) = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + mQ = _mm_andnot_ps( mQ, *(LLQuad*)(allOnes) ); + return *this; + } + + inline LLBool32 areAllSet( U32 mask ) const + { + return ( getGatheredBits() & mask) == mask; + } + + inline LLBool32 areAllSet() const + { + return areAllSet( MASK_XYZW ); + } + + inline LLBool32 areAnySet( U32 mask ) const + { + return getGatheredBits() & mask; + } + + inline LLBool32 areAnySet() const + { + return areAnySet( MASK_XYZW ); + } + + inline operator LLQuad() const + { + return mQ; + } + + inline void clear() + { + mQ = _mm_setzero_ps(); + } + + template void setElement() + { + mQ = _mm_or_ps( mQ, *reinterpret_cast(S_V4LOGICAL_MASK_TABLE + 4*N) ); + } + +private: + + LLQuad mQ; +}; + +#endif //LL_VECTOR4ALOGICAL_H diff --git a/indra/llmath/llvolumeoctree.cpp b/indra/llmath/llvolumeoctree.cpp new file mode 100644 index 0000000000..194b1faf81 --- /dev/null +++ b/indra/llmath/llvolumeoctree.cpp @@ -0,0 +1,208 @@ +/** + + * @file llvolumeoctree.cpp + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llvolumeoctree.h" +#include "llvector4a.h" + +BOOL LLLineSegmentBoxIntersect(const LLVector4a& start, const LLVector4a& end, const LLVector4a& center, const LLVector4a& size) +{ + LLVector4a fAWdU; + LLVector4a dir; + LLVector4a diff; + + dir.setSub(end, start); + dir.mul(0.5f); + + diff.setAdd(end,start); + diff.mul(0.5f); + diff.sub(center); + fAWdU.setAbs(dir); + + LLVector4a rhs; + rhs.setAdd(size, fAWdU); + + LLVector4a lhs; + lhs.setAbs(diff); + + U32 grt = lhs.greaterThan(rhs).getGatheredBits(); + + if (grt & 0x7) + { + return false; + } + + LLVector4a f; + f.setCross3(dir, diff); + f.setAbs(f); + + LLVector4a v0, v1; + + v0 = _mm_shuffle_ps(size, size,_MM_SHUFFLE(3,0,0,1)); + v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,1,2,2)); + lhs.setMul(v0, v1); + + v0 = _mm_shuffle_ps(size, size, _MM_SHUFFLE(3,1,2,2)); + v1 = _mm_shuffle_ps(fAWdU, fAWdU, _MM_SHUFFLE(3,0,0,1)); + rhs.setMul(v0, v1); + rhs.add(lhs); + + grt = f.greaterThan(rhs).getGatheredBits(); + + return (grt & 0x7) ? false : true; +} + + +LLVolumeOctreeListener::LLVolumeOctreeListener(LLOctreeNode* node) +{ + node->addListener(this); + + mBounds = (LLVector4a*) ll_aligned_malloc_16(sizeof(LLVector4a)*4); + mExtents = mBounds+2; +} + +LLVolumeOctreeListener::~LLVolumeOctreeListener() +{ + ll_aligned_free_16(mBounds); +} + +void LLVolumeOctreeListener::handleChildAddition(const LLOctreeNode* parent, + LLOctreeNode* child) +{ + new LLVolumeOctreeListener(child); +} + + +LLOctreeTriangleRayIntersect::LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, + const LLVolumeFace* face, F32* closest_t, + LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal) + : mFace(face), + mStart(start), + mDir(dir), + mIntersection(intersection), + mTexCoord(tex_coord), + mNormal(normal), + mBinormal(bi_normal), + mClosestT(closest_t), + mHitFace(false) +{ + mEnd.setAdd(mStart, mDir); +} + +void LLOctreeTriangleRayIntersect::traverse(const LLOctreeNode* node) +{ + LLVolumeOctreeListener* vl = (LLVolumeOctreeListener*) node->getListener(0); + + /*const F32* start = mStart.getF32(); + const F32* end = mEnd.getF32(); + const F32* center = vl->mBounds[0].getF32(); + const F32* size = vl->mBounds[1].getF32();*/ + + //if (LLLineSegmentBoxIntersect(mStart.getF32(), mEnd.getF32(), vl->mBounds[0].getF32(), vl->mBounds[1].getF32())) + if (LLLineSegmentBoxIntersect(mStart, mEnd, vl->mBounds[0], vl->mBounds[1])) + { + node->accept(this); + for (S32 i = 0; i < node->getChildCount(); ++i) + { + traverse(node->getChild(i)); + } + } +} + +void LLOctreeTriangleRayIntersect::visit(const LLOctreeNode* node) +{ + for (LLOctreeNode::const_element_iter iter = + node->getData().begin(); iter != node->getData().end(); ++iter) + { + const LLVolumeTriangle* tri = *iter; + + F32 a, b, t; + + if (LLTriangleRayIntersect(*tri->mV[0], *tri->mV[1], *tri->mV[2], + mStart, mDir, a, b, t)) + { + if ((t >= 0.f) && // if hit is after start + (t <= 1.f) && // and before end + (t < *mClosestT)) // and this hit is closer + { + *mClosestT = t; + mHitFace = true; + + if (mIntersection != NULL) + { + LLVector4a intersect = mDir; + intersect.mul(*mClosestT); + intersect.add(mStart); + mIntersection->set(intersect.getF32ptr()); + } + + + if (mTexCoord != NULL) + { + LLVector2* tc = (LLVector2*) mFace->mTexCoords; + *mTexCoord = ((1.f - a - b) * tc[tri->mIndex[0]] + + a * tc[tri->mIndex[1]] + + b * tc[tri->mIndex[2]]); + + } + + if (mNormal != NULL) + { + LLVector4* norm = (LLVector4*) mFace->mNormals; + + *mNormal = ((1.f - a - b) * LLVector3(norm[tri->mIndex[0]]) + + a * LLVector3(norm[tri->mIndex[1]]) + + b * LLVector3(norm[tri->mIndex[2]])); + } + + if (mBinormal != NULL) + { + LLVector4* binormal = (LLVector4*) mFace->mBinormals; + *mBinormal = ((1.f - a - b) * LLVector3(binormal[tri->mIndex[0]]) + + a * LLVector3(binormal[tri->mIndex[1]]) + + b * LLVector3(binormal[tri->mIndex[2]])); + } + } + } + } +} + +const LLVector4a& LLVolumeTriangle::getPositionGroup() const +{ + return *mPositionGroup; +} + +const F32& LLVolumeTriangle::getBinRadius() const +{ + return mRadius; +} + + diff --git a/indra/llmath/llvolumeoctree.h b/indra/llmath/llvolumeoctree.h new file mode 100644 index 0000000000..0031626498 --- /dev/null +++ b/indra/llmath/llvolumeoctree.h @@ -0,0 +1,138 @@ +/** + * @file llvolumeoctree.h + * @brief LLVolume octree classes. + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLVOLUME_OCTREE_H +#define LL_LLVOLUME_OCTREE_H + +#include "linden_common.h" +#include "llmemory.h" + +#include "lloctree.h" +#include "llvolume.h" +#include "llvector4a.h" + +class LLVolumeOctreeListener : public LLOctreeListener +{ +public: + + LLVolumeOctreeListener(LLOctreeNode* node); + ~LLVolumeOctreeListener(); + + LLVolumeOctreeListener(const LLVolumeOctreeListener& rhs) + { + *this = rhs; + } + + const LLVolumeOctreeListener& operator=(const LLVolumeOctreeListener& rhs) + { + llerrs << "Illegal operation!" << llendl; + return *this; + } + + //LISTENER FUNCTIONS + virtual void handleChildAddition(const LLOctreeNode* parent, + LLOctreeNode* child); + virtual void handleStateChange(const LLTreeNode* node) { } + virtual void handleChildRemoval(const LLOctreeNode* parent, + const LLOctreeNode* child) { } + virtual void handleInsertion(const LLTreeNode* node, LLVolumeTriangle* tri) { } + virtual void handleRemoval(const LLTreeNode* node, LLVolumeTriangle* tri) { } + virtual void handleDestruction(const LLTreeNode* node) { } + + +public: + LLVector4a* mBounds; // bounding box (center, size) of this node and all its children (tight fit to objects) + LLVector4a* mExtents; // extents (min, max) of this node and all its children +}; + +class LLOctreeTriangleRayIntersect : public LLOctreeTraveler +{ +public: + const LLVolumeFace* mFace; + LLVector4a mStart; + LLVector4a mDir; + LLVector4a mEnd; + LLVector3* mIntersection; + LLVector2* mTexCoord; + LLVector3* mNormal; + LLVector3* mBinormal; + F32* mClosestT; + bool mHitFace; + + LLOctreeTriangleRayIntersect() { }; + + LLOctreeTriangleRayIntersect(const LLVector4a& start, const LLVector4a& dir, + const LLVolumeFace* face, F32* closest_t, + LLVector3* intersection,LLVector2* tex_coord, LLVector3* normal, LLVector3* bi_normal); + + void traverse(const LLOctreeNode* node); + + virtual void visit(const LLOctreeNode* node); +}; + +class LLVolumeTriangle : public LLRefCount +{ +public: + LLVolumeTriangle() + { + mPositionGroup = (LLVector4a*) ll_aligned_malloc_16(16); + } + + LLVolumeTriangle(const LLVolumeTriangle& rhs) + { + *this = rhs; + } + + const LLVolumeTriangle& operator=(const LLVolumeTriangle& rhs) + { + llerrs << "Illegal operation!" << llendl; + return *this; + } + + ~LLVolumeTriangle() + { + ll_aligned_free_16(mPositionGroup); + } + + const LLVector4a* mV[3]; + U16 mIndex[3]; + + LLVector4a* mPositionGroup; + + F32 mRadius; + + virtual const LLVector4a& getPositionGroup() const; + virtual const F32& getBinRadius() const; +}; + + +#endif diff --git a/indra/llprimitive/llmodel.cpp b/indra/llprimitive/llmodel.cpp new file mode 100644 index 0000000000..82765c740f --- /dev/null +++ b/indra/llprimitive/llmodel.cpp @@ -0,0 +1,1695 @@ +/** + * @file llmodel.cpp + * @brief Model handling implementation + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llmodel.h" +#include "llsdserialize.h" +#include "llvector4a.h" + +#include "dae.h" +#include "dae/daeErrorHandler.h" +#include "dom/domConstants.h" +#include "dom/domMesh.h" +#include "zlib/zlib.h" + +#if LL_MESH_ENABLED + +std::string model_names[] = +{ + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod", + "physics_shape" +}; + +const int MODEL_NAMES_LENGTH = sizeof(model_names) / sizeof(std::string); + +LLModel::LLModel(LLVolumeParams& params, F32 detail) + : LLVolume(params, detail), mNormalizedScale(1,1,1), mNormalizedTranslation(0,0,0) +{ + +} + +void load_face_from_dom_inputs(LLVolumeFace& face, const domInputLocalOffset_Array& inputs, U32 min_idx, U32 max_idx) +{ + for (U32 j = 0; j < inputs.getCount(); ++j) + { + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0) + { //found vertex array + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + + domInputLocal_Array& v_inp = vertices->getInput_array(); + if (inputs[j]->getOffset() != 0) + { + llerrs << "WTF?" << llendl; + } + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + const domURIFragmentType& uri = v_inp[k]->getSource(); + + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + + if (src->getTechnique_common()->getAccessor()->getStride() != 3) + { + llerrs << "WTF?" << llendl; + } + + domListOfFloats& v = src->getFloat_array()->getValue(); + + LLVector4a min; + min.set(v[min_idx], v[min_idx+1], v[min_idx+2]); + LLVector4a max = min; + + for (U32 j = min_idx; j <= max_idx; ++j) + { //copy vertex array + face.mPositions[j-min_idx].set(v[j*3+0], v[j*3+1], v[j*3+2]); + update_min_max(min, max, face.mPositions[j-min_idx]); + } + + face.mExtents[0] = min; + face.mExtents[1] = max; + } + } + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) + { + //found normal array for this triangle list + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + domListOfFloats& n = src->getFloat_array()->getValue(); + + for (U32 j = min_idx; j <= max_idx; ++j) + { + LLVector4a* norm = (LLVector4a*) face.mNormals + (j-min_idx); + norm->set(n[j*3+0], n[j*3+1], n[j*3+2]); + norm->normalize3fast(); + } + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) + { //found texCoords + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + domListOfFloats& u = src->getFloat_array()->getValue(); + + for (U32 j = min_idx; j <= max_idx; ++j) + { + face.mTexCoords[j-min_idx].setVec(u[j*2+0], u[j*2+1]); + } + } + } +} + +void get_dom_sources(const domInputLocalOffset_Array& inputs, S32& pos_offset, S32& tc_offset, S32& norm_offset, S32 &idx_stride, + domSource* &pos_source, domSource* &tc_source, domSource* &norm_source) +{ + idx_stride = 0; + + for (U32 j = 0; j < inputs.getCount(); ++j) + { + idx_stride = llmax((S32) inputs[j]->getOffset(), idx_stride); + + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[j]->getSemantic()) == 0) + { //found vertex array + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + + domInputLocal_Array& v_inp = vertices->getInput_array(); + + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + pos_offset = inputs[j]->getOffset(); + + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + pos_source = (domSource*) elem.cast(); + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, v_inp[k]->getSemantic()) == 0) + { + norm_offset = inputs[j]->getOffset(); + + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + norm_source = (domSource*) elem.cast(); + } + } + } + + if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[j]->getSemantic()) == 0) + { + //found normal array for this triangle list + norm_offset = inputs[j]->getOffset(); + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + norm_source = (domSource*) elem.cast(); + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[j]->getSemantic()) == 0) + { //found texCoords + tc_offset = inputs[j]->getOffset(); + const domURIFragmentType& uri = inputs[j]->getSource(); + daeElementRef elem = uri.getElement(); + tc_source = (domSource*) elem.cast(); + } + } + + idx_stride += 1; +} + +void load_face_from_dom_triangles(std::vector& face_list, std::vector& materials, domTrianglesRef& tri) +{ + LLVolumeFace face; + std::vector verts; + std::vector indices; + + const domInputLocalOffset_Array& inputs = tri->getInput_array(); + + S32 pos_offset = -1; + S32 tc_offset = -1; + S32 norm_offset = -1; + + domSource* pos_source = NULL; + domSource* tc_source = NULL; + domSource* norm_source = NULL; + + S32 idx_stride = 0; + + get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source); + + domPRef p = tri->getP(); + domListOfUInts& idx = p->getValue(); + + domListOfFloats v; + domListOfFloats tc; + domListOfFloats n; + + if (pos_source) + { + v = pos_source->getFloat_array()->getValue(); + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + } + + if (tc_source) + { + tc = tc_source->getFloat_array()->getValue(); + } + + if (norm_source) + { + n = norm_source->getFloat_array()->getValue(); + } + + + LLVolumeFace::VertexMapData::PointMap point_map; + + for (U32 i = 0; i < idx.getCount(); i += idx_stride) + { + LLVolumeFace::VertexData cv; + if (pos_source) + { + cv.setPosition(LLVector4a(v[idx[i+pos_offset]*3+0], + v[idx[i+pos_offset]*3+1], + v[idx[i+pos_offset]*3+2])); + } + + if (tc_source) + { + cv.mTexCoord.setVec(tc[idx[i+tc_offset]*2+0], + tc[idx[i+tc_offset]*2+1]); + } + + if (norm_source) + { + cv.setNormal(LLVector4a(n[idx[i+norm_offset]*3+0], + n[idx[i+norm_offset]*3+1], + n[idx[i+norm_offset]*3+2])); + } + + + BOOL found = FALSE; + + LLVolumeFace::VertexMapData::PointMap::iterator point_iter; + point_iter = point_map.find(LLVector3(cv.getPosition().getF32ptr())); + + if (point_iter != point_map.end()) + { + for (U32 j = 0; j < point_iter->second.size(); ++j) + { + if ((point_iter->second)[j] == cv) + { + found = TRUE; + indices.push_back((point_iter->second)[j].mIndex); + break; + } + } + } + + if (!found) + { + update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); + verts.push_back(cv); + if (verts.size() >= 65535) + { + llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl; + } + U16 index = (U16) (verts.size()-1); + indices.push_back(index); + + LLVolumeFace::VertexMapData d; + d.setPosition(cv.getPosition()); + d.mTexCoord = cv.mTexCoord; + d.setNormal(cv.getNormal()); + d.mIndex = index; + if (point_iter != point_map.end()) + { + point_iter->second.push_back(d); + } + else + { + point_map[LLVector3(d.getPosition().getF32ptr())].push_back(d); + } + } + + if (indices.size()%3 == 0 && verts.size() >= 65532) + { + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + face = LLVolumeFace(); + point_map.clear(); + } + + } + + if (!verts.empty()) + { + std::string material; + + if (tri->getMaterial()) + { + material = std::string(tri->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + + face_list.rbegin()->fillFromLegacyData(verts, indices); + } + +} + +void load_face_from_dom_polylist(std::vector& face_list, std::vector& materials, domPolylistRef& poly) +{ + domPRef p = poly->getP(); + domListOfUInts& idx = p->getValue(); + + if (idx.getCount() == 0) + { + return; + } + + const domInputLocalOffset_Array& inputs = poly->getInput_array(); + + + domListOfUInts& vcount = poly->getVcount()->getValue(); + + S32 pos_offset = -1; + S32 tc_offset = -1; + S32 norm_offset = -1; + + domSource* pos_source = NULL; + domSource* tc_source = NULL; + domSource* norm_source = NULL; + + S32 idx_stride = 0; + + get_dom_sources(inputs, pos_offset, tc_offset, norm_offset, idx_stride, pos_source, tc_source, norm_source); + + LLVolumeFace face; + + std::vector indices; + std::vector verts; + + domListOfFloats v; + domListOfFloats tc; + domListOfFloats n; + + if (pos_source) + { + v = pos_source->getFloat_array()->getValue(); + face.mExtents[0].set(v[0], v[1], v[2]); + face.mExtents[1].set(v[0], v[1], v[2]); + } + + if (tc_source) + { + tc = tc_source->getFloat_array()->getValue(); + } + + if (norm_source) + { + n = norm_source->getFloat_array()->getValue(); + } + + LLVolumeFace::VertexMapData::PointMap point_map; + + U32 cur_idx = 0; + for (U32 i = 0; i < vcount.getCount(); ++i) + { //for each polygon + U32 first_index = 0; + U32 last_index = 0; + for (U32 j = 0; j < vcount[i]; ++j) + { //for each vertex + + LLVolumeFace::VertexData cv; + + if (pos_source) + { + cv.getPosition().set(v[idx[cur_idx+pos_offset]*3+0], + v[idx[cur_idx+pos_offset]*3+1], + v[idx[cur_idx+pos_offset]*3+2]); + } + + if (tc_source) + { + cv.mTexCoord.setVec(tc[idx[cur_idx+tc_offset]*2+0], + tc[idx[cur_idx+tc_offset]*2+1]); + } + + if (norm_source) + { + cv.getNormal().set(n[idx[cur_idx+norm_offset]*3+0], + n[idx[cur_idx+norm_offset]*3+1], + n[idx[cur_idx+norm_offset]*3+2]); + } + + cur_idx += idx_stride; + + BOOL found = FALSE; + + LLVolumeFace::VertexMapData::PointMap::iterator point_iter; + LLVector3 pos3(cv.getPosition().getF32ptr()); + point_iter = point_map.find(pos3); + + if (point_iter != point_map.end()) + { + for (U32 k = 0; k < point_iter->second.size(); ++k) + { + if ((point_iter->second)[k] == cv) + { + found = TRUE; + U32 index = (point_iter->second)[k].mIndex; + if (j == 0) + { + first_index = index; + } + else if (j == 1) + { + last_index = index; + } + else + { + indices.push_back(first_index); + indices.push_back(last_index); + indices.push_back(index); + last_index = index; + } + + break; + } + } + } + + if (!found) + { + update_min_max(face.mExtents[0], face.mExtents[1], cv.getPosition()); + verts.push_back(cv); + if (verts.size() >= 65535) + { + llerrs << "Attempted to write model exceeding 16-bit index buffer limitation." << llendl; + } + U16 index = (U16) (verts.size()-1); + + if (j == 0) + { + first_index = index; + } + else if (j == 1) + { + last_index = index; + } + else + { + indices.push_back(first_index); + indices.push_back(last_index); + indices.push_back(index); + last_index = index; + } + + LLVolumeFace::VertexMapData d; + d.setPosition(cv.getPosition()); + d.mTexCoord = cv.mTexCoord; + d.setNormal(cv.getNormal()); + d.mIndex = index; + if (point_iter != point_map.end()) + { + point_iter->second.push_back(d); + } + else + { + point_map[pos3].push_back(d); + } + } + + if (indices.size()%3 == 0 && indices.size() >= 65532) + { + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + face = LLVolumeFace(); + verts.clear(); + indices.clear(); + point_map.clear(); + } + } + } + + if (!verts.empty()) + { + std::string material; + + if (poly->getMaterial()) + { + material = std::string(poly->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + } +} + +void load_face_from_dom_polygons(std::vector& face_list, std::vector& materials, domPolygonsRef& poly) +{ + LLVolumeFace face; + std::vector indices; + std::vector verts; + + const domInputLocalOffset_Array& inputs = poly->getInput_array(); + + + S32 v_offset = -1; + S32 n_offset = -1; + S32 t_offset = -1; + + domListOfFloats* v = NULL; + domListOfFloats* n = NULL; + domListOfFloats* t = NULL; + + U32 stride = 0; + for (U32 i = 0; i < inputs.getCount(); ++i) + { + stride = llmax((U32) inputs[i]->getOffset()+1, stride); + + if (strcmp(COMMON_PROFILE_INPUT_VERTEX, inputs[i]->getSemantic()) == 0) + { //found vertex array + v_offset = inputs[i]->getOffset(); + + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domVertices* vertices = (domVertices*) elem.cast(); + + domInputLocal_Array& v_inp = vertices->getInput_array(); + + for (U32 k = 0; k < v_inp.getCount(); ++k) + { + if (strcmp(COMMON_PROFILE_INPUT_POSITION, v_inp[k]->getSemantic()) == 0) + { + const domURIFragmentType& uri = v_inp[k]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + v = &(src->getFloat_array()->getValue()); + } + } + } + else if (strcmp(COMMON_PROFILE_INPUT_NORMAL, inputs[i]->getSemantic()) == 0) + { + n_offset = inputs[i]->getOffset(); + //found normal array for this triangle list + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + n = &(src->getFloat_array()->getValue()); + } + else if (strcmp(COMMON_PROFILE_INPUT_TEXCOORD, inputs[i]->getSemantic()) == 0 && inputs[i]->getSet() == 0) + { //found texCoords + t_offset = inputs[i]->getOffset(); + const domURIFragmentType& uri = inputs[i]->getSource(); + daeElementRef elem = uri.getElement(); + domSource* src = (domSource*) elem.cast(); + t = &(src->getFloat_array()->getValue()); + } + } + + domP_Array& ps = poly->getP_array(); + + //make a triangle list in + for (U32 i = 0; i < ps.getCount(); ++i) + { //for each polygon + domListOfUInts& idx = ps[i]->getValue(); + for (U32 j = 0; j < idx.getCount()/stride; ++j) + { //for each vertex + if (j > 2) + { + U32 size = verts.size(); + LLVolumeFace::VertexData v0 = verts[size-3]; + LLVolumeFace::VertexData v1 = verts[size-1]; + + verts.push_back(v0); + verts.push_back(v1); + } + + LLVolumeFace::VertexData vert; + + + if (v) + { + U32 v_idx = idx[j*stride+v_offset]*3; + vert.getPosition().set(v->get(v_idx), + v->get(v_idx+1), + v->get(v_idx+2)); + } + + if (n) + { + U32 n_idx = idx[j*stride+n_offset]*3; + vert.getNormal().set(n->get(n_idx), + n->get(n_idx+1), + n->get(n_idx+2)); + } + + if (t) + { + U32 t_idx = idx[j*stride+t_offset]*2; + vert.mTexCoord.setVec(t->get(t_idx), + t->get(t_idx+1)); + } + + + verts.push_back(vert); + } + } + + if (verts.empty()) + { + return; + } + + face.mExtents[0] = verts[0].getPosition(); + face.mExtents[1] = verts[0].getPosition(); + + //create a map of unique vertices to indices + std::map vert_idx; + + U32 cur_idx = 0; + for (U32 i = 0; i < verts.size(); ++i) + { + std::map::iterator iter = vert_idx.find(verts[i]); + if (iter == vert_idx.end()) + { + vert_idx[verts[i]] = cur_idx++; + } + } + + //build vertex array from map + verts.resize(vert_idx.size()); + + for (std::map::iterator iter = vert_idx.begin(); iter != vert_idx.end(); ++iter) + { + verts[iter->second] = iter->first; + update_min_max(face.mExtents[0], face.mExtents[1], iter->first.getPosition()); + } + + //build index array from map + indices.resize(verts.size()); + + for (U32 i = 0; i < verts.size(); ++i) + { + indices[i] = vert_idx[verts[i]]; + } + + + if (!verts.empty()) + { + std::string material; + + if (poly->getMaterial()) + { + material = std::string(poly->getMaterial()); + } + + materials.push_back(material); + face_list.push_back(face); + face_list.rbegin()->fillFromLegacyData(verts, indices); + } +} + +void LLModel::addVolumeFacesFromDomMesh(domMesh* mesh) +{ + domTriangles_Array& tris = mesh->getTriangles_array(); + + for (U32 i = 0; i < tris.getCount(); ++i) + { + domTrianglesRef& tri = tris.get(i); + + load_face_from_dom_triangles(mVolumeFaces, mMaterialList, tri); + } + + domPolylist_Array& polys = mesh->getPolylist_array(); + for (U32 i = 0; i < polys.getCount(); ++i) + { + domPolylistRef& poly = polys.get(i); + + load_face_from_dom_polylist(mVolumeFaces, mMaterialList, poly); + } + + domPolygons_Array& polygons = mesh->getPolygons_array(); + for (U32 i = 0; i < polygons.getCount(); ++i) + { + domPolygonsRef& poly = polygons.get(i); + + load_face_from_dom_polygons(mVolumeFaces, mMaterialList, poly); + } + +} + +BOOL LLModel::createVolumeFacesFromDomMesh(domMesh* mesh) +{ + if (mesh) + { + mVolumeFaces.clear(); + mMaterialList.clear(); + + addVolumeFacesFromDomMesh(mesh); + + if (getNumVolumeFaces() > 0) + { + optimizeVolumeFaces(); + normalizeVolumeFaces(); + + if (getNumVolumeFaces() > 0) + { + return TRUE; + } + } + } + else + { + llwarns << "no mesh found" << llendl; + } + + return FALSE; +} + + +BOOL LLModel::createVolumeFacesFromFile(const std::string& file_name) +{ + DAE dae; + domCOLLADA* dom = dae.open(file_name); + if (dom) + { + daeDatabase* db = dae.getDatabase(); + + daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); + + mVolumeFaces.clear(); + mMaterialList.clear(); + + for (daeInt idx = 0; idx < count; ++idx) + { + domMesh* mesh = NULL; + + db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH); + + if (mesh) + { + addVolumeFacesFromDomMesh(mesh); + } + } + + if (getNumVolumeFaces() > 0) + { + optimizeVolumeFaces(); + normalizeVolumeFaces(); + return TRUE; + } + } + + return FALSE; +} + + +void LLModel::optimizeVolumeFaces() +{ +#if 0 //VECTORIZE ? + for (std::vector::iterator iter = mVolumeFaces.begin(); iter != mVolumeFaces.end(); ) + { + std::vector::iterator cur_iter = iter++; + LLVolumeFace& face = *cur_iter; + + for (S32 i = 0; i < (S32) face.mNumIndices; i += 3) + { //remove zero area triangles + U16 i0 = face.mIndices[i+0]; + U16 i1 = face.mIndices[i+1]; + U16 i2 = face.mIndices[i+2]; + + if (i0 == i1 || + i1 == i2 || + i0 == i2) + { //duplicate index in triangle, remove triangle + face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3); + i -= 3; + } + else + { + LLVolumeFace::VertexData& v0 = face.mVertices[i0]; + LLVolumeFace::VertexData& v1 = face.mVertices[i1]; + LLVolumeFace::VertexData& v2 = face.mVertices[i2]; + + if (v0.mPosition == v1.mPosition || + v1.mPosition == v2.mPosition || + v2.mPosition == v0.mPosition) + { //zero area triangle, delete + face.mIndices.erase(face.mIndices.begin()+i, face.mIndices.begin()+i+3); + i-=3; + } + } + } + + //remove unreference vertices + std::vector ref; + ref.resize(face.mNumVertices); + + for (U32 i = 0; i < ref.size(); ++i) + { + ref[i] = false; + } + + for (U32 i = 0; i < face.mNumIndices; ++i) + { + ref[face.mIndices[i]] = true; + } + + U32 unref_count = 0; + for (U32 i = 0; i < ref.size(); ++i) + { + if (!ref[i]) + { + //vertex is unreferenced + face.mVertices.erase(face.mVertices.begin()+(i-unref_count)); + U16 idx = (U16) (i-unref_count); + + for (U32 j = 0; j < face.mNumIndices; ++j) + { //decrement every index array value greater than idx + if (face.mIndices[j] > idx) + { + --face.mIndices[j]; + } + } + ++unref_count; + } + } + + if (face.mVertices.empty() || face.mIndices.empty()) + { //face is empty, remove it + iter = mVolumeFaces.erase(cur_iter); + } + } +#endif +} + +void LLModel::normalizeVolumeFaces() +{ + + // ensure we don't have too many faces + if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES) + mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES); + + if (!mVolumeFaces.empty()) + { + LLVector4a min, max; + + if (mVolumeFaces[0].mNumVertices <= 0) + { + llerrs << "WTF?" << llendl; + } + + min = mVolumeFaces[0].mExtents[0]; + max = mVolumeFaces[0].mExtents[1]; + + for (U32 i = 1; i < mVolumeFaces.size(); ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + if (face.mNumVertices <= 0) + { + llerrs << "WTF?" << llendl; + } + + update_min_max(min, max, face.mExtents[0]); + update_min_max(min, max, face.mExtents[1]); + } + + LLVector4a trans; + trans.setAdd(min, max); + trans.mul(-0.5f); + LLVector4a size; + size.setSub(max, min); + + F32 scale = 1.f/llmax(llmax(size[0], size[1]), size[2]); + + for (U32 i = 0; i < mVolumeFaces.size(); ++i) + { + LLVolumeFace& face = mVolumeFaces[i]; + + face.mExtents[0].add(trans); + face.mExtents[0].mul(scale); + + face.mExtents[1].add(trans); + face.mExtents[1].mul(scale); + + LLVector4a* pos = (LLVector4a*) face.mPositions; + for (U32 j = 0; j < face.mNumVertices; ++j) + { + pos[j].add(trans); + pos[j].mul(scale); + } + } + + mNormalizedScale = LLVector3(1,1,1) / scale; + mNormalizedTranslation.set(trans.getF32ptr()); + mNormalizedTranslation *= -1.f; + } +} + +void LLModel::getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out) +{ + scale_out = mNormalizedScale; + translation_out = mNormalizedTranslation; +} + +void LLModel::setNumVolumeFaces(S32 count) +{ + mVolumeFaces.resize(count); +} + +void LLModel::setVolumeFaceData(S32 f, + LLStrider pos, + LLStrider norm, + LLStrider tc, + LLStrider ind, + U32 num_verts, + U32 num_indices) +{ + LLVolumeFace& face = mVolumeFaces[f]; + + face.resizeVertices(num_verts); + face.resizeIndices(num_indices); + + LLVector4a::memcpyNonAliased16((F32*) face.mPositions, (F32*) pos.get(), num_verts*4*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) face.mNormals, (F32*) norm.get(), num_verts*4*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) face.mTexCoords, (F32*) tc.get(), num_verts*2*sizeof(F32)); + U32 size = (num_indices*2+0xF)&~0xF; + LLVector4a::memcpyNonAliased16((F32*) face.mIndices, (F32*) ind.get(), size); +} + +void LLModel::appendFaces(LLModel *model, LLMatrix4 &transform, LLMatrix4& norm_mat) +{ + if (mVolumeFaces.empty()) + { + setNumVolumeFaces(1); + } + + LLVolumeFace& face = mVolumeFaces[mVolumeFaces.size()-1]; + + + for (S32 i = 0; i < model->getNumFaces(); ++i) + { + face.appendFace(model->getVolumeFace(i), transform, norm_mat); + } + +} + +void LLModel::appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat) +{ + S32 rindex = getNumVolumeFaces()-1; + if (rindex == -1 || + mVolumeFaces[rindex].mNumVertices + src_face.mNumVertices >= 65536) + { //empty or overflow will occur, append new face + LLVolumeFace cur_face; + cur_face.appendFace(src_face, mat, norm_mat); + addFace(cur_face); + mMaterialList.push_back(src_material); + } + else + { //append to existing end face + mVolumeFaces.rbegin()->appendFace(src_face, mat, norm_mat); + } +} + +void LLModel::addFace(const LLVolumeFace& face) +{ + if (face.mNumVertices == 0) + { + llerrs << "Cannot add empty face." << llendl; + } + + mVolumeFaces.push_back(face); + + if (mVolumeFaces.size() > MAX_MODEL_FACES) + { + llerrs << "Model prims cannot have more than " << MAX_MODEL_FACES << " faces!" << llendl; + } +} + + +void LLModel::smoothNormals(F32 angle_cutoff) +{ + //smooth normals for all faces by: + // 1 - Create faceted copy of face with no texture coordinates + // 2 - Weld vertices in faceted copy that are shared between triangles with less than "angle_cutoff" difference between normals + // 3 - Generate smoothed set of normals based on welding results + // 4 - Create faceted copy of face with texture coordinates + // 5 - Copy smoothed normals to faceted copy, using closest normal to triangle normal where more than one normal exists for a given position + // 6 - Remove redundant vertices from new faceted (now smooth) copy + + angle_cutoff = cosf(angle_cutoff); + for (U32 j = 0; j < mVolumeFaces.size(); ++j) + { + LLVolumeFace& vol_face = mVolumeFaces[j]; + + //create faceted copy of current face with no texture coordinates (step 1) + LLVolumeFace faceted; + + LLVector4a* src_pos = (LLVector4a*) vol_face.mPositions; + //LLVector4a* src_norm = (LLVector4a*) vol_face.mNormals; + + + //bake out triangles into temporary face, clearing normals and texture coordinates + for (U32 i = 0; i < vol_face.mNumIndices; ++i) + { + U32 idx = vol_face.mIndices[i]; + LLVolumeFace::VertexData v; + v.setPosition(src_pos[idx]); + v.getNormal().clear(); + v.mTexCoord.clear(); + faceted.pushVertex(v); + faceted.pushIndex(i); + } + + //generate normals for temporary face + for (U32 i = 0; i < faceted.mNumIndices; i += 3) + { //for each triangle + U16 i0 = faceted.mIndices[i+0]; + U16 i1 = faceted.mIndices[i+1]; + U16 i2 = faceted.mIndices[i+2]; + + LLVector4a& p0 = faceted.mPositions[i0]; + LLVector4a& p1 = faceted.mPositions[i1]; + LLVector4a& p2 = faceted.mPositions[i2]; + + LLVector4a& n0 = faceted.mNormals[i0]; + LLVector4a& n1 = faceted.mNormals[i1]; + LLVector4a& n2 = faceted.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + n0.setCross3(lhs, rhs); + n0.normalize3fast(); + n1 = n0; + n2 = n0; + } + + //weld vertices in temporary face, respecting angle_cutoff (step 2) + faceted.optimize(angle_cutoff); + + //generate normals for welded face based on new topology (step 3) + + for (U32 i = 0; i < faceted.mNumVertices; i++) + { + faceted.mNormals[i].clear(); + } + + for (U32 i = 0; i < faceted.mNumIndices; i += 3) + { //for each triangle + U16 i0 = faceted.mIndices[i+0]; + U16 i1 = faceted.mIndices[i+1]; + U16 i2 = faceted.mIndices[i+2]; + + LLVector4a& p0 = faceted.mPositions[i0]; + LLVector4a& p1 = faceted.mPositions[i1]; + LLVector4a& p2 = faceted.mPositions[i2]; + + LLVector4a& n0 = faceted.mNormals[i0]; + LLVector4a& n1 = faceted.mNormals[i1]; + LLVector4a& n2 = faceted.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + LLVector4a n; + n.setCross3(lhs, rhs); + + n0.add(n); + n1.add(n); + n2.add(n); + } + + //normalize normals and build point map + LLVolumeFace::VertexMapData::PointMap point_map; + + for (U32 i = 0; i < faceted.mNumVertices; ++i) + { + faceted.mNormals[i].normalize3fast(); + + LLVolumeFace::VertexMapData v; + v.setPosition(faceted.mPositions[i]); + v.setNormal(faceted.mNormals[i]); + + point_map[LLVector3(v.getPosition().getF32ptr())].push_back(v); + } + + //create faceted copy of current face with texture coordinates (step 4) + LLVolumeFace new_face; + + //bake out triangles into new face + for (U32 i = 0; i < vol_face.mNumIndices; ++i) + { + U32 idx = vol_face.mIndices[i]; + LLVolumeFace::VertexData v; + v.setPosition(vol_face.mPositions[idx]); + v.setNormal(vol_face.mNormals[idx]); + v.mTexCoord = vol_face.mTexCoords[idx]; + + new_face.pushVertex(v); + new_face.pushIndex(i); + } + + //generate normals for new face + for (U32 i = 0; i < new_face.mNumIndices; i += 3) + { //for each triangle + U16 i0 = new_face.mIndices[i+0]; + U16 i1 = new_face.mIndices[i+1]; + U16 i2 = new_face.mIndices[i+2]; + + LLVector4a& p0 = new_face.mPositions[i0]; + LLVector4a& p1 = new_face.mPositions[i1]; + LLVector4a& p2 = new_face.mPositions[i2]; + + LLVector4a& n0 = new_face.mNormals[i0]; + LLVector4a& n1 = new_face.mNormals[i1]; + LLVector4a& n2 = new_face.mNormals[i2]; + + LLVector4a lhs, rhs; + lhs.setSub(p1, p0); + rhs.setSub(p2, p0); + + n0.setCross3(lhs, rhs); + n0.normalize3fast(); + n1 = n0; + n2 = n0; + } + + //swap out normals in new_face with best match from point map (step 5) + for (U32 i = 0; i < new_face.mNumVertices; ++i) + { + //LLVolumeFace::VertexData v = new_face.mVertices[i]; + + LLVector4a ref_norm = new_face.mNormals[i]; + + LLVolumeFace::VertexMapData::PointMap::iterator iter = point_map.find(LLVector3(new_face.mPositions[i].getF32ptr())); + + if (iter != point_map.end()) + { + F32 best = -2.f; + for (U32 k = 0; k < iter->second.size(); ++k) + { + LLVector4a& n = iter->second[k].getNormal(); + + if (!iter->second[k].getPosition().equals3(new_face.mPositions[i])) + { + llerrs << "WTF?" << llendl; + } + + F32 cur = n.dot3(ref_norm).getF32(); + + if (cur > best) + { + best = cur; + new_face.mNormals[i] = n; + } + } + } + } + + //remove redundant vertices from new face (step 6) + new_face.optimize(); + + mVolumeFaces[j] = new_face; + } +} + +//static +std::string LLModel::getElementLabel(daeElement *element) +{ // try to get a decent label for this element + // if we have a name attribute, use it + std::string name = element->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if we have an ID attribute, use it + if (element->getID()) + { + return std::string(element->getID()); + } + + // if we have a parent, use it + daeElement* parent = element->getParent(); + if (parent) + { + // if parent has a name, use it + std::string name = parent->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if parent has an ID, use it + if (parent->getID()) + { + return std::string(parent->getID()); + } + } + + // try to use our type + daeString element_name = element->getElementName(); + if (element_name) + { + return std::string(element_name); + } + + // if all else fails, use "object" + return std::string("object"); +} + +//static +LLModel* LLModel::loadModelFromDae(std::string filename) +{ + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + LLModel* ret = new LLModel(volume_params, 0.f); + ret->createVolumeFacesFromFile(filename); + return ret; +} + +//static +LLModel* LLModel::loadModelFromDomMesh(domMesh *mesh) +{ + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + LLModel* ret = new LLModel(volume_params, 0.f); + ret->createVolumeFacesFromDomMesh(mesh); + ret->mLabel = getElementLabel(mesh); + return ret; +} + +//static +LLSD LLModel::writeModel(std::string filename, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* impostor, LLModel::physics_shape& decomp, BOOL nowrite) +{ + std::ofstream os(filename.c_str(), std::ofstream::out | std::ofstream::binary); + + LLSD header = writeModel(os, physics, high, medium, low, impostor, decomp, nowrite); + + os.close(); + + return header; +} + +//static +LLSD LLModel::writeModel(std::ostream& ostr, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* impostor, LLModel::physics_shape& decomp, BOOL nowrite) +{ + LLSD mdl; + + LLModel* model[] = + { + impostor, + low, + medium, + high, + physics + }; + + bool skinning = high && !high->mSkinWeights.empty(); + + if (skinning) + { //write skinning block + if (high->mJointList.size() != high->mInvBindMatrix.size()) + { + llerrs << "WTF?" << llendl; + } + + for (U32 i = 0; i < high->mJointList.size(); ++i) + { + mdl["skin"]["joint_names"][i] = high->mJointList[i]; + + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mdl["skin"]["inverse_bind_matrix"][i][j*4+k] = high->mInvBindMatrix[i].mMatrix[j][k]; + } + } + } + + for (U32 i = 0; i < 4; i++) + { + for (U32 j = 0; j < 4; j++) + { + mdl["skin"]["bind_shape_matrix"][i*4+j] = high->mBindShapeMatrix.mMatrix[i][j]; + } + } + } + + if (!decomp.empty()) + { + //write decomposition block + // ["decomposition"]["HullList"] -- list of 8 bit integers, each entry represents a hull with specified number of points + // ["decomposition"]["PositionDomain"]["Min"/"Max"] + // ["decomposition"]["Position"] -- list of 16-bit integers to be decoded to given domain, encoded 3D points + + //get minimum and maximum + LLVector3 min = decomp[0][0]; + LLVector3 max = min; + + LLSD::Binary hulls(decomp.size()); + + U32 total = 0; + + for (U32 i = 0; i < decomp.size(); ++i) + { + U32 size = decomp[i].size(); + total += size; + hulls[i] = (U8) size; + + for (U32 j = 0; j < decomp[i].size(); ++j) + { + update_min_max(min, max, decomp[i][j]); + } + } + + mdl["decomposition"]["Min"] = min.getValue(); + mdl["decomposition"]["Max"] = max.getValue(); + mdl["decomposition"]["HullList"] = hulls; + + LLSD::Binary p(total*3*2); + + LLVector3 range = max-min; + + U32 vert_idx = 0; + for (U32 i = 0; i < decomp.size(); ++i) + { + for (U32 j = 0; j < decomp[i].size(); ++j) + { + for (U32 k = 0; k < 3; k++) + { + //convert to 16-bit normalized across domain + U16 val = (U16) (((decomp[i][j].mV[k]-min.mV[k])/range.mV[k])*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + p[vert_idx++] = buff[0]; + p[vert_idx++] = buff[1]; + + if (vert_idx > p.size()) + { + llerrs << "WTF?" << llendl; + } + } + } + } + + mdl["decomposition"]["Position"] = p; + } + + for (U32 idx = 0; idx < MODEL_NAMES_LENGTH; ++idx) + { + if (model[idx] && model[idx]->getNumVolumeFaces() > 0) + { + LLVector3 min_pos = LLVector3(model[idx]->getVolumeFace(0).mPositions[0].getF32ptr()); + LLVector3 max_pos = min_pos; + + //find position domain + for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i) + { //for each face + const LLVolumeFace& face = model[idx]->getVolumeFace(i); + for (U32 j = 0; j < face.mNumVertices; ++j) + { + update_min_max(min_pos, max_pos, face.mPositions[j].getF32ptr()); + } + } + + LLVector3 pos_range = max_pos - min_pos; + + for (S32 i = 0; i < model[idx]->getNumVolumeFaces(); ++i) + { //for each face + const LLVolumeFace& face = model[idx]->getVolumeFace(i); + if (!face.mNumVertices) + { //don't export an empty face + continue; + } + LLSD::Binary verts(face.mNumVertices*3*2); + LLSD::Binary tc(face.mNumVertices*2*2); + LLSD::Binary normals(face.mNumVertices*3*2); + LLSD::Binary indices(face.mNumIndices*2); + + U32 vert_idx = 0; + U32 norm_idx = 0; + U32 tc_idx = 0; + + LLVector2* ftc = (LLVector2*) face.mTexCoords; + LLVector2 min_tc = ftc[0]; + LLVector2 max_tc = min_tc; + + //get texture coordinate domain + for (U32 j = 0; j < face.mNumVertices; ++j) + { + update_min_max(min_tc, max_tc, ftc[j]); + } + + LLVector2 tc_range = max_tc - min_tc; + + for (U32 j = 0; j < face.mNumVertices; ++j) + { //for each vert + + F32* pos = face.mPositions[j].getF32ptr(); + F32* norm = face.mNormals[j].getF32ptr(); + + //position + normal + for (U32 k = 0; k < 3; ++k) + { //for each component + + //convert to 16-bit normalized across domain + U16 val = (U16) (((pos[k]-min_pos.mV[k])/pos_range.mV[k])*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + verts[vert_idx++] = buff[0]; + verts[vert_idx++] = buff[1]; + + //convert to 16-bit normalized + val = (U16) ((norm[k]+1.f)*0.5f*65535); + + //write to binary buffer + normals[norm_idx++] = buff[0]; + normals[norm_idx++] = buff[1]; + } + + F32* src_tc = (F32*) face.mTexCoords[j].mV; + + //texcoord + for (U32 k = 0; k < 2; ++k) + { //for each component + //convert to 16-bit normalized + U16 val = (U16) ((src_tc[k]-min_tc.mV[k])/tc_range.mV[k]*65535); + + U8* buff = (U8*) &val; + //write to binary buffer + tc[tc_idx++] = buff[0]; + tc[tc_idx++] = buff[1]; + } + + } + + U32 idx_idx = 0; + for (U32 j = 0; j < face.mNumIndices; ++j) + { + U8* buff = (U8*) &(face.mIndices[j]); + indices[idx_idx++] = buff[0]; + indices[idx_idx++] = buff[1]; + } + + //write out face data + mdl[model_names[idx]][i]["PositionDomain"]["Min"] = min_pos.getValue(); + mdl[model_names[idx]][i]["PositionDomain"]["Max"] = max_pos.getValue(); + + mdl[model_names[idx]][i]["TexCoord0Domain"]["Min"] = min_tc.getValue(); + mdl[model_names[idx]][i]["TexCoord0Domain"]["Max"] = max_tc.getValue(); + + mdl[model_names[idx]][i]["Position"] = verts; + mdl[model_names[idx]][i]["Normal"] = normals; + mdl[model_names[idx]][i]["TexCoord0"] = tc; + mdl[model_names[idx]][i]["TriangleList"] = indices; + + if (skinning) + { + //write out skin weights + + //each influence list entry is up to 4 24-bit values + // first 8 bits is bone index + // last 16 bits is bone influence weight + // a bone index of 0xFF signifies no more influences for this vertex + + std::stringstream ostr; + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + LLVector3 pos(face.mPositions[j].getF32ptr()); + + weight_list& weights = high->getJointInfluences(pos); + + if (weights.size() > 4) + { + llerrs << "WTF?" << llendl; + } + + S32 count = 0; + for (weight_list::iterator iter = weights.begin(); iter != weights.end(); ++iter) + { + if (iter->mJointIdx < 255 && iter->mJointIdx >= 0) + { + U8 idx = (U8) iter->mJointIdx; + ostr.write((const char*) &idx, 1); + + U16 influence = (U16) (iter->mWeight*65535); + ostr.write((const char*) &influence, 2); + + ++count; + } + } + U8 end_list = 0xFF; + if (count < 4) + { + ostr.write((const char*) &end_list, 1); + } + } + + //copy ostr to binary buffer + std::string data = ostr.str(); + const U8* buff = (U8*) data.data(); + U32 bytes = data.size(); + + LLSD::Binary w(bytes); + for (U32 j = 0; j < bytes; ++j) + { + w[j] = buff[j]; + } + + mdl[model_names[idx]][i]["Weights"] = w; + } + } + } + } + + return writeModelToStream(ostr, mdl, nowrite); +} + +LLSD LLModel::writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite) +{ + U32 bytes = 0; + + std::string::size_type cur_offset = 0; + + LLSD header; + + std::string skin; + + if (mdl.has("skin")) + { //write out skin block + skin = zip_llsd(mdl["skin"]); + + U32 size = skin.size(); + if (size > 0) + { + header["skin"]["offset"] = (LLSD::Integer) cur_offset; + header["skin"]["size"] = (LLSD::Integer) size; + cur_offset += size; + bytes += size; + } + else + { + llerrs << "WTF?" << llendl; + } + } + + std::string decomposition; + + if (mdl.has("decomposition")) + { //write out convex decomposition + decomposition = zip_llsd(mdl["decomposition"]); + + U32 size = decomposition.size(); + if (size > 0) + { + header["decomposition"]["offset"] = (LLSD::Integer) cur_offset; + header["decomposition"]["size"] = (LLSD::Integer) size; + cur_offset += size; + bytes += size; + } + } + + std::string out[MODEL_NAMES_LENGTH]; + + for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++) + { + if (mdl.has(model_names[i])) + { + out[i] = zip_llsd(mdl[model_names[i]]); + + U32 size = out[i].size(); + + header[model_names[i]]["offset"] = (LLSD::Integer) cur_offset; + header[model_names[i]]["size"] = (LLSD::Integer) size; + cur_offset += size; + bytes += size; + } + else + { + header[model_names[i]]["offset"] = -1; + header[model_names[i]]["size"] = 0; + } + } + + if (!nowrite) + { + LLSDSerialize::serialize(header, ostr, LLSDSerialize::LLSD_BINARY); + + if (!skin.empty()) + { //write skin block + ostr.write((const char*) skin.data(), header["skin"]["size"].asInteger()); + } + + if (!decomposition.empty()) + { //write decomposition block + ostr.write((const char*) decomposition.data(), header["decomposition"]["size"].asInteger()); + } + + for (S32 i = 0; i < MODEL_NAMES_LENGTH; i++) + { + if (!out[i].empty()) + { + ostr.write((const char*) out[i].data(), header[model_names[i]]["size"].asInteger()); + } + } + } + + return header; +} + +//static +LLModel* LLModel::loadModelFromAsset(std::string filename, S32 lod) +{ + return NULL; +} + +LLModel::weight_list& LLModel::getJointInfluences(const LLVector3& pos) +{ + weight_map::iterator iter = mSkinWeights.find(pos); + + if (iter != mSkinWeights.end()) + { + if ((iter->first - pos).magVec() > 0.1f) + { + llerrs << "WTF?" << llendl; + } + + return iter->second; + } + else + { //no exact match found, get closest point + iter = mSkinWeights.begin(); + weight_map::iterator best = iter; + + F32 min_dist = (iter->first - pos).magVecSquared(); + + while (++iter != mSkinWeights.end()) + { + F32 dist = (iter->first - pos).magVecSquared(); + if (dist < min_dist) + { + best = iter; + min_dist = dist; + } + } + + return best->second; + } +} + +#endif + diff --git a/indra/llprimitive/llmodel.h b/indra/llprimitive/llmodel.h new file mode 100644 index 0000000000..a91c80d5b7 --- /dev/null +++ b/indra/llprimitive/llmodel.h @@ -0,0 +1,179 @@ +/** + * @file llmodel.h + * @brief Model handling class definitions + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLMODEL_H +#define LL_LLMODEL_H + +#include "llvolume.h" +#include "v4math.h" +#include "m4math.h" + +#if LL_MESH_ENABLED + +class daeElement; +class domMesh; + +#define MAX_MODEL_FACES 8 + +class LLModel : public LLVolume +{ +public: + + enum + { + LOD_IMPOSTOR = 0, + LOD_LOW, + LOD_MEDIUM, + LOD_HIGH, + LOD_PHYSICS, + NUM_LODS + }; + + //physics shape is a vector of convex hulls + //each convex hull is a set of points + typedef std::vector > physics_shape; + + LLModel(LLVolumeParams& params, F32 detail); + static LLSD writeModel(std::string filename, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* imposotr, LLModel::physics_shape& physics_shape, BOOL nowrite = FALSE); + static LLSD writeModel(std::ostream& ostr, LLModel* physics, LLModel* high, LLModel* medium, LLModel* low, LLModel* imposotr, LLModel::physics_shape& physics_shape, BOOL nowrite = FALSE); + static LLSD writeModelToStream(std::ostream& ostr, LLSD& mdl, BOOL nowrite = FALSE); + static LLModel* loadModelFromAsset(std::string filename, S32 lod); + static LLModel* loadModelFromDae(std::string filename); + static LLModel* loadModelFromDomMesh(domMesh* mesh); + static std::string getElementLabel(daeElement* element); + + void appendFaces(LLModel* model, LLMatrix4& transform, LLMatrix4& normal_transform); + void appendFace(const LLVolumeFace& src_face, std::string src_material, LLMatrix4& mat, LLMatrix4& norm_mat); + + void setNumVolumeFaces(S32 count); + void setVolumeFaceData(S32 f, + LLStrider pos, + LLStrider norm, + LLStrider tc, + LLStrider ind, + U32 num_verts, + U32 num_indices); + + void smoothNormals(F32 angle_cutoff); + + void addFace(const LLVolumeFace& face); + + void normalizeVolumeFaces(); + void optimizeVolumeFaces(); + + + U32 getResourceCost(); + void getNormalizedScaleTranslation(LLVector3& scale_out, LLVector3& translation_out); + std::vector mMaterialList; + + //data used for skin weights + class JointWeight + { + public: + S32 mJointIdx; + F32 mWeight; + + JointWeight() + { + mJointIdx = 0; + mWeight = 0.f; + } + + JointWeight(S32 idx, F32 weight) + : mJointIdx(idx), mWeight(weight) + { + } + + bool operator<(const JointWeight& rhs) const + { + if (mWeight == rhs.mWeight) + { + return mJointIdx < rhs.mJointIdx; + } + + return mWeight < rhs.mWeight; + } + + }; + + struct CompareWeightGreater + { + bool operator()(const JointWeight& lhs, const JointWeight& rhs) + { + return rhs < lhs; // strongest = first + } + }; + + //copy of position array for this model -- mPosition[idx].mV[X,Y,Z] + std::vector mPosition; + + //map of positions to skin weights --- mSkinWeights[pos].mV[0..4] == . + //joint_index corresponds to mJointList + typedef std::vector weight_list; + typedef std::map weight_map; + weight_map mSkinWeights; + + //get list of weight influences closest to given position + weight_list& getJointInfluences(const LLVector3& pos); + + //should always be true that mJointList[mJointMap["foo"]] == "foo" + + //map of joint names to joint index + std::map mJointMap; + + //list of joint names + std::vector mJointList; + + LLMatrix4 mBindShapeMatrix; + std::vector mInvBindMatrix; + + std::string mLabel; + + LLVector3 mNormalizedScale; + LLVector3 mNormalizedTranslation; + + //physics shape + physics_shape mPhysicsShape; + + LLVector3 mPhysicsCenter; + std::vector mHullCenter; + U32 mPhysicsPoints; + +protected: + void addVolumeFacesFromDomMesh(domMesh* mesh); + virtual BOOL createVolumeFacesFromFile(const std::string& file_name); + virtual BOOL createVolumeFacesFromDomMesh(domMesh *mesh); +}; + + +#endif + +#endif //LL_LLMODEL_H diff --git a/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl new file mode 100644 index 0000000000..eef6556fba --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/avatar/objectSkinV.glsl @@ -0,0 +1,30 @@ +/** + * @file objectSkinV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +attribute vec4 object_weight; + +uniform mat4 matrixPalette[64]; + +mat4 getObjectSkinnedTransform() +{ + int i; + + vec4 w = fract(object_weight); + vec4 index = floor(object_weight); + + float scale = 1.0/(w.x+w.y+w.z+w.w); + w *= scale; + + mat4 mat = matrixPalette[int(index.x)]*w.x; + mat += matrixPalette[int(index.y)]*w.y; + mat += matrixPalette[int(index.z)]*w.z; + mat += matrixPalette[int(index.w)]*w.w; + + return mat; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl new file mode 100644 index 0000000000..fde0e97713 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/alphaSkinnedV.glsl @@ -0,0 +1,75 @@ +/** + * @file alphaSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +mat4 getObjectSkinnedTransform(); +void calcAtmospherics(vec3 inPositionEye); + +float calcDirectionalLight(vec3 n, vec3 l); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); + +vec3 atmosAmbient(vec3 light); +vec3 atmosAffectDirectionalLight(float lightIntensity); +vec3 scaleDownLight(vec3 light); +vec3 scaleUpLight(vec3 light); + +varying vec3 vary_position; +varying vec3 vary_ambient; +varying vec3 vary_directional; +varying vec3 vary_normal; +varying vec3 vary_light; + +void main() +{ + gl_TexCoord[0] = gl_MultiTexCoord0; + + vec4 pos; + vec3 norm; + + mat4 trans = getObjectSkinnedTransform(); + trans = gl_ModelViewMatrix * trans; + + pos = trans * gl_Vertex; + + norm = gl_Vertex.xyz + gl_Normal.xyz; + norm = normalize(( trans*vec4(norm, 1.0) ).xyz-pos.xyz); + + gl_Position = gl_ProjectionMatrix * pos; + + vary_position = pos.xyz; + vary_normal = norm; + + calcAtmospherics(pos.xyz); + + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); + + // Collect normal lights (need to be divided by two, as we later multiply by 2) + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); + col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); + col.rgb = scaleDownLight(col.rgb); + + // Add windlight lights + col.rgb += atmosAmbient(vec3(0.)); + + vary_light = gl_LightSource[0].position.xyz; + + vary_ambient = col.rgb*gl_Color.rgb; + vary_directional = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a))); + + col.rgb = min(col.rgb*gl_Color.rgb, 1.0); + + gl_FrontColor = col; + + gl_FogFragCoord = pos.z; +} + + diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl new file mode 100644 index 0000000000..085ffddeec --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowF.glsl @@ -0,0 +1,16 @@ +/** + * @file avatarShadowF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +uniform sampler2D diffuseMap; + + +void main() +{ + //gl_FragColor = vec4(1,1,1,gl_Color.a * texture2D(diffuseMap, gl_TexCoord[0].xy).a); + gl_FragColor = vec4(1,1,1,1); +} + diff --git a/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl new file mode 100644 index 0000000000..1626e21cd8 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/attachmentShadowV.glsl @@ -0,0 +1,25 @@ +/** + * @file diffuseSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +mat4 getObjectSkinnedTransform(); + +void main() +{ + //transform vertex + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + gl_FrontColor = gl_Color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl new file mode 100644 index 0000000000..d884f2e4a5 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/bumpSkinnedV.glsl @@ -0,0 +1,37 @@ +/** + * @file bumpV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +varying vec3 vary_mat0; +varying vec3 vary_mat1; +varying vec3 vary_mat2; + +mat4 getObjectSkinnedTransform(); + +void main() +{ + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + + vec3 pos = (mat*gl_Vertex).xyz; + + + vec3 n = normalize((mat * vec4(gl_Normal.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz); + vec3 b = normalize((mat * vec4(gl_MultiTexCoord2.xyz+gl_Vertex.xyz, 1.0)).xyz-pos.xyz); + vec3 t = cross(b, n); + + vary_mat0 = vec3(t.x, b.x, n.x); + vary_mat1 = vec3(t.y, b.y, n.y); + vary_mat2 = vec3(t.z, b.z, n.z); + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + gl_FrontColor = gl_Color; +} diff --git a/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl new file mode 100644 index 0000000000..9a45c03237 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/deferred/diffuseSkinnedV.glsl @@ -0,0 +1,33 @@ +/** + * @file diffuseSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +varying vec3 vary_normal; + +mat4 getObjectSkinnedTransform(); + +void main() +{ + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + vary_normal = norm.xyz; + + gl_FrontColor = gl_Color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl new file mode 100644 index 0000000000..4cde013eef --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/lighting/lightFullbrightShinyWaterF.glsl @@ -0,0 +1,15 @@ +/** + * @file lightFullbrightShinyWaterF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + + +uniform sampler2D diffuseMap; +uniform samplerCube environmentMap; + +void fullbright_shiny_lighting_water() +{ + gl_FragColor = texture2D(diffuseMap, gl_TexCoord[0].xy); +} diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl new file mode 100644 index 0000000000..f0baeeeee5 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinySkinnedV.glsl @@ -0,0 +1,39 @@ +/** + * @file shinySimpleSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + vec3 ref = reflect(pos.xyz, -norm.xyz); + + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0); + + calcAtmospherics(pos.xyz); + + gl_FrontColor = gl_Color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + + gl_FogFragCoord = pos.z; +} diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl new file mode 100644 index 0000000000..5e6e4c16b7 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightShinyWaterF.glsl @@ -0,0 +1,13 @@ +/** + * @file fullbrightShinyWaterF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +void fullbright_shiny_lighting_water(); + +void main() +{ + fullbright_shiny_lighting_water(); +} diff --git a/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl new file mode 100644 index 0000000000..02ff3cc2a9 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/fullbrightSkinnedV.glsl @@ -0,0 +1,37 @@ +/** + * @file fullbrightSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + //transform vertex + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + calcAtmospherics(pos.xyz); + + gl_FrontColor = gl_Color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + + gl_FogFragCoord = pos.z; +} diff --git a/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl new file mode 100644 index 0000000000..4146646058 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/shinySimpleSkinnedV.glsl @@ -0,0 +1,39 @@ +/** + * @file shinySimpleSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + vec3 ref = reflect(pos.xyz, -norm.xyz); + + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + gl_TexCoord[1] = gl_TextureMatrix[1]*vec4(ref,1.0); + + calcAtmospherics(pos.xyz); + + vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.)); + gl_FrontColor = color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); +} diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl new file mode 100644 index 0000000000..59944b8861 --- /dev/null +++ b/indra/newview/app_settings/shaders/class1/objects/simpleSkinnedV.glsl @@ -0,0 +1,39 @@ +/** + * @file simpleV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#version 120 + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +void calcAtmospherics(vec3 inPositionEye); +mat4 getObjectSkinnedTransform(); + +attribute vec4 object_weight; + +void main() +{ + //transform vertex + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + vec3 pos = (mat*gl_Vertex).xyz; + + vec4 norm = gl_Vertex; + norm.xyz += gl_Normal.xyz; + norm.xyz = (mat*norm).xyz; + norm.xyz = normalize(norm.xyz-pos.xyz); + + calcAtmospherics(pos.xyz); + + vec4 color = calcLighting(pos.xyz, norm.xyz, gl_Color, vec4(0.)); + gl_FrontColor = color; + + gl_Position = gl_ProjectionMatrix*vec4(pos, 1.0); + + gl_FogFragCoord = pos.z; +} diff --git a/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl new file mode 100644 index 0000000000..dc4663677b --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/deferred/alphaSkinnedV.glsl @@ -0,0 +1,84 @@ +/** + * @file alphaSkinnedV.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol); +void calcAtmospherics(vec3 inPositionEye); + +float calcDirectionalLight(vec3 n, vec3 l); +float calcPointLightOrSpotLight(vec3 v, vec3 n, vec4 lp, vec3 ln, float la, float is_pointlight); +mat4 getObjectSkinnedTransform(); +vec3 atmosAmbient(vec3 light); +vec3 atmosAffectDirectionalLight(float lightIntensity); +vec3 scaleDownLight(vec3 light); +vec3 scaleUpLight(vec3 light); + +varying vec3 vary_ambient; +varying vec3 vary_directional; +varying vec3 vary_fragcoord; +varying vec3 vary_position; +varying vec3 vary_light; + +uniform float near_clip; +uniform float shadow_offset; +uniform float shadow_bias; + +void main() +{ + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; + + mat4 mat = getObjectSkinnedTransform(); + + mat = gl_ModelViewMatrix * mat; + + vec3 pos = (mat*gl_Vertex).xyz; + + gl_Position = gl_ProjectionMatrix * vec4(pos, 1.0); + + vec4 n = gl_Vertex; + n.xyz += gl_Normal.xyz; + n.xyz = (mat*n).xyz; + n.xyz = normalize(n.xyz-pos.xyz); + + vec3 norm = n.xyz; + + float dp_directional_light = max(0.0, dot(norm, gl_LightSource[0].position.xyz)); + vary_position = pos.xyz + gl_LightSource[0].position.xyz * (1.0-dp_directional_light)*shadow_offset; + + calcAtmospherics(pos.xyz); + + //vec4 color = calcLighting(pos.xyz, norm, gl_Color, vec4(0.)); + vec4 col = vec4(0.0, 0.0, 0.0, gl_Color.a); + + // Collect normal lights (need to be divided by two, as we later multiply by 2) + col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[2].position, gl_LightSource[2].spotDirection.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].specular.a); + col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[3].position, gl_LightSource[3].spotDirection.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].specular.a); + col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[4].position, gl_LightSource[4].spotDirection.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].specular.a); + col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[5].position, gl_LightSource[5].spotDirection.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].specular.a); + col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[6].position, gl_LightSource[6].spotDirection.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].specular.a); + col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLightOrSpotLight(pos.xyz, norm, gl_LightSource[7].position, gl_LightSource[7].spotDirection.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].specular.a); + col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz); + col.rgb = scaleDownLight(col.rgb); + + // Add windlight lights + col.rgb += atmosAmbient(vec3(0.)); + + vary_light = gl_LightSource[0].position.xyz; + + vary_ambient = col.rgb*gl_Color.rgb; + vary_directional.rgb = gl_Color.rgb*atmosAffectDirectionalLight(max(calcDirectionalLight(norm, gl_LightSource[0].position.xyz), (1.0-gl_Color.a)*(1.0-gl_Color.a))); + + col.rgb = min(col.rgb*gl_Color.rgb, 1.0); + + gl_FrontColor = col; + + gl_FogFragCoord = pos.z; + + pos.xyz = (gl_ModelViewProjectionMatrix * gl_Vertex).xyz; + vary_fragcoord.xyz = pos.xyz + vec3(0,0,near_clip); + +} + diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl new file mode 100644 index 0000000000..eca9a567f6 --- /dev/null +++ b/indra/newview/app_settings/shaders/class2/lighting/lightFullbrightShinyWaterF.glsl @@ -0,0 +1,29 @@ +/** + * @file lightFullbrightShinyWaterF.glsl + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +uniform sampler2D diffuseMap; +uniform samplerCube environmentMap; + +vec3 fullbrightShinyAtmosTransport(vec3 light); +vec3 fullbrightScaleSoftClip(vec3 light); +vec4 applyWaterFog(vec4 color); + +void fullbright_shiny_lighting_water() +{ + vec4 color = texture2D(diffuseMap, gl_TexCoord[0].xy); + color.rgb *= gl_Color.rgb; + + vec3 envColor = textureCube(environmentMap, gl_TexCoord[1].xyz).rgb; + color.rgb = mix(color.rgb, envColor.rgb, gl_Color.a); + + color.rgb = fullbrightShinyAtmosTransport(color.rgb); + color.rgb = fullbrightScaleSoftClip(color.rgb); + color.a = max(color.a, gl_Color.a); + + gl_FragColor = applyWaterFog(color); +} + diff --git a/indra/newview/llfloaterimportcollada.cpp b/indra/newview/llfloaterimportcollada.cpp new file mode 100644 index 0000000000..476c02cd46 --- /dev/null +++ b/indra/newview/llfloaterimportcollada.cpp @@ -0,0 +1,1116 @@ +/** + * @file llfloaterimportcollada.cpp + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloaterimportcollada.h" + +#include "dae.h" +//#include "dom.h" +#include "dom/domAsset.h" +#include "dom/domBind_material.h" +#include "dom/domConstants.h" +#include "dom/domEffect.h" +#include "dom/domGeometry.h" +#include "dom/domInstance_geometry.h" +#include "dom/domInstance_material.h" +#include "dom/domInstance_node.h" +#include "dom/domInstance_effect.h" +#include "dom/domMaterial.h" +#include "dom/domMatrix.h" +#include "dom/domNode.h" +#include "dom/domProfile_COMMON.h" +#include "dom/domRotate.h" +#include "dom/domScale.h" +#include "dom/domTranslate.h" +#include "dom/domVisual_scene.h" + +#include "llagent.h" +#include "llassetuploadresponders.h" +#include "lleconomy.h" +#include "llfloaterperms.h" +#include "llfloaterreg.h" +#include "llsdutil.h" +#include "llsdutil_math.h" +#include "llselectmgr.h" +#include "llvfile.h" +#include "llvfs.h" +#include "llviewermenufile.h" +#include "llviewerregion.h" +#include "llvolumemessage.h" +#include "llmodel.h" +#include "llmeshreduction.h" +#include "material_codes.h" + +// +// floater +// + +#if LL_MESH_ENABLED + +LLFloaterImportCollada::LLFloaterImportCollada(const LLSD& key) + : LLFloater(key) +{ +} + + +BOOL LLFloaterImportCollada::postBuild() +{ + if (!LLFloater::postBuild()) + { + return FALSE; + } + + childSetCommitCallback("ok", LLImportCollada::onCommitOK, this); + childSetCommitCallback("cancel", LLImportCollada::onCommitCancel, this); + + setStatusIdle(); + setAssetCount(0,0); + enableOK(TRUE); + + return TRUE; +} + + +void LLFloaterImportCollada::setAssetCount(S32 mesh_count, S32 texture_count) +{ + childSetTextArg("mesh count", "[COUNT]", llformat("%d", mesh_count)); + childSetTextArg("texture count", "[COUNT]", llformat("%d", texture_count)); +} + +void LLFloaterImportCollada::setStatusAssetUploading(std::string asset_name) +{ + LLUIString uploading = getString("status_uploading"); + uploading.setArg("[NAME]", asset_name); + childSetTextArg("status", "[STATUS]", uploading.getString()); +} + +void LLFloaterImportCollada::setStatusCreatingPrim(std::string prim_name) +{ + LLUIString creating = getString("status_creating"); + creating.setArg("[NAME]", prim_name); + childSetTextArg("status", "[STATUS]", creating.getString()); +} + +void LLFloaterImportCollada::setStatusIdle() +{ + childSetTextArg("status", "[STATUS]", getString("status_idle")); +} + +void LLFloaterImportCollada::enableOK(BOOL enable) +{ + childSetEnabled("ok", enable); +} + + +// +// misc helpers +// + + +// why oh why do forbid matrix multiplication in our llmath library? +LLMatrix4 matrix_multiply(LLMatrix4 a, LLMatrix4 b) +{ + a *= b; + + return a; +} + +// why oh why does colladadom not provide such things? +daeElement* getFirstChild(daeElement* parent) +{ + daeTArray< daeSmartRef > children = parent->getChildren(); + + if (children.getCount() > 0) + { + return children[0]; + } + else + { + return NULL; + } +} + +// why oh why does colladadom not provide such things? +daeElement* getNextSibling(daeElement* child) +{ + daeElement* parent = child->getParent(); + + if (parent == NULL) + { + // must be root, root has no siblings + return NULL; + } + + daeElement* sibling = NULL; + + daeTArray< daeSmartRef > children = parent->getChildren(); + for (S32 i = 0; i < children.getCount(); i++) + { + if (child == children[i]) + { + if ((i+1) < children.getCount()) + { + sibling = children[i+1]; + } + } + } + + return sibling; +} + +// try to get a decent label for this element +std::string getElementLabel(daeElement *element) +{ + // if we have a name attribute, use it + std::string name = element->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if we have an ID attribute, use it + if (element->getID()) + { + return std::string(element->getID()); + } + + // if we have a parent, use it + daeElement* parent = element->getParent(); + if (parent) + { + // if parent has a name, use it + std::string name = parent->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if parent has an ID, use it + if (parent->getID()) + { + return std::string(parent->getID()); + } + } + + // try to use our type + daeString element_name = element->getElementName(); + if (element_name) + { + return std::string(element_name); + } + + // if all else fails, use "object" + return std::string("object"); +} + +LLColor4 getDaeColor(daeElement* element) +{ + LLColor4 value; + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast(element->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + } + + return value; +} + +LLTextureEntry profileToTextureEntry(domProfile_COMMON* material) +{ + LLTextureEntry te; + + te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture + daeElement* diffuse = material->getDescendant("diffuse"); + if (diffuse) + { + te.setColor(LLColor3(0.1f, 0.9f, 1.0f)); + + domCommon_color_or_texture_type_complexType::domTexture* texture = + daeSafeCast(diffuse->getDescendant("texture")); + if (texture) + { + domCommon_newparam_type_Array newparams = material->getNewparam_array(); + for (S32 i = 0; i < newparams.getCount(); i++) + { + domFx_surface_common* surface = newparams[i]->getSurface(); + if (surface) + { + domFx_surface_init_common* init = surface->getFx_surface_init_common(); + if (init) + { + domFx_surface_init_from_common_Array init_from = init->getInit_from_array(); + + if (init_from.getCount() > 0) + { + daeElement* image = init_from[0]->getValue().getElement(); + if (image) + { + LLUUID texture_asset = LLImportColladaAssetCache::getInstance()->getAssetForDaeElement(image); + + if (texture_asset.notNull()) + { + te.setID(texture_asset); + te.setColor(LLColor3(1,1,1)); + } + } + } + } + } + } + } + + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast(diffuse->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + te.setColor(value); + } + } + + daeElement* emission = material->getDescendant("emission"); + if (emission) + { + LLColor4 emission_color = getDaeColor(emission); + if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25) + { + te.setFullbright(TRUE); + } + } + + return te; +} + +std::vector getMaterials(LLModel* model, domInstance_geometry* instance_geo) +{ + std::vector texture_entries; + for (int i = 0; i < model->mMaterialList.size(); i++) + { + LLTextureEntry texture_entry; + + domInstance_material* instance_mat = NULL; + + domBind_material::domTechnique_common* technique = + daeSafeCast(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); + + if (technique) + { + daeTArray< daeSmartRef > inst_materials = technique->getChildrenByType(); + for (int j = 0; j < inst_materials.getCount(); j++) + { + std::string symbol(inst_materials[j]->getSymbol()); + + if (symbol == model->mMaterialList[i]) // found the binding + { + instance_mat = inst_materials[j]; + } + } + } + + if (instance_mat) + { + domMaterial* material = daeSafeCast(instance_mat->getTarget().getElement()); + if (material) + { + domInstance_effect* instance_effect = + daeSafeCast(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); + if (instance_effect) + { + domEffect* effect = daeSafeCast(instance_effect->getUrl().getElement()); + if (effect) + { + domProfile_COMMON* profile = + daeSafeCast(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID()))); + if (profile) + { + texture_entry = profileToTextureEntry(profile); + } + } + } + } + } + + texture_entries.push_back(texture_entry); + } + + return texture_entries; +} + +LLTextureEntry instanceGeoToTextureEntry(domInstance_geometry* instance_geo) +{ + LLTextureEntry te; + domInstance_material* instance_mat = + daeSafeCast(instance_geo->getDescendant(daeElement::matchType(domInstance_material::ID()))); + if (instance_mat) + { + } + + return te; +} + + + +// responder for asset uploads +// does all the normal stuff followed by a notification to continue importing +// WARNING - currently unused - TODO +class LLColladaNewAgentInventoryResponder : public LLNewAgentInventoryResponder +{ + LLColladaNewAgentInventoryResponder(const LLSD& post_data, + const LLUUID& vfile_id, + LLAssetType::EType asset_type) + : LLNewAgentInventoryResponder(post_data, vfile_id, asset_type) + { + } + LLColladaNewAgentInventoryResponder(const LLSD& post_data, + const std::string& file_name, + LLAssetType::EType asset_type) + : LLNewAgentInventoryResponder(post_data, file_name, asset_type) + { + } + + virtual void uploadComplete(const LLSD& content) + { + LLNewAgentInventoryResponder::uploadComplete(content); + } + +}; + +BOOL LLImportColladaAssetCache::uploadImageAsset(domImage* image) +{ + // we only support init_from now - embedded data will come later + domImage::domInit_from* init = image->getInit_from(); + if (!init) + { + return FALSE; + } + + std::string filename = cdom::uriToNativePath(init->getValue().str()); + + std::string name = getElementLabel(image); + + LLUUID transaction_id = upload_new_resource(filename, name, std::string(), + 0, LLFolderType::FT_TEXTURE, LLInventoryType::IT_TEXTURE, + LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), + LLFloaterPerms::getEveryonePerms(), + name, NULL, + LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), NULL); + + if (transaction_id.isNull()) + { + llwarns << "cannot upload " << filename << llendl; + + return FALSE; + } + + mTransactionMap[transaction_id] = image; + + LLFloaterReg::findTypedInstance("import_collada")->setStatusAssetUploading(name); + + return TRUE; +} + + + +// +// asset cache - +// uploads assets and provides a map from collada element to asset +// + + + +BOOL LLImportColladaAssetCache::uploadMeshAsset(domMesh* mesh) +{ + LLPointer model = LLModel::loadModelFromDomMesh(mesh); + + if (model->getNumVolumeFaces() == 0) + { + return FALSE; + } + + // generate LODs + + + std::vector > lods; + lods.push_back(model); + S32 triangle_count = model->getNumTriangles(); + + for (S32 i = 0; i < 4; i++) + { + LLPointer last_model = lods.back(); + + S32 triangle_target = (S32)(triangle_count / pow(3.f, i + 1)); + if (triangle_target > 16) + { + LLMeshReduction reduction; + LLPointer new_model = reduction.reduce(model, triangle_target, LLMeshReduction::TRIANGLE_BUDGET); + lods.push_back(new_model); + } + else + { + lods.push_back(last_model); + } + } + + // write model to temp file + + std::string filename = gDirUtilp->getTempFilename(); + LLModel::writeModel(filename, + lods[4], + lods[0], + lods[1], + lods[2], + lods[3], + lods[4]->mPhysicsShape); + + + // copy file to VFS + + LLTransactionID tid; + tid.generate(); + LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID()); // create asset uuid + + S32 file_size; + LLAPRFile infile ; + infile.open(filename, LL_APR_RB, NULL, &file_size); + + if (infile.getFileHandle()) + { + LLVFile file(gVFS, uuid, LLAssetType::AT_MESH, LLVFile::WRITE); + + file.setMaxSize(file_size); + + const S32 buf_size = 65536; + U8 copy_buf[buf_size]; + while ((file_size = infile.read(copy_buf, buf_size))) + { + file.write(copy_buf, file_size); + } + } + + + std::string name = getElementLabel(mesh); + + upload_new_resource(tid, LLAssetType::AT_MESH, name, std::string(), 0,LLFolderType::FT_MESH, LLInventoryType::IT_MESH, + LLFloaterPerms::getNextOwnerPerms(), LLFloaterPerms::getGroupPerms(), LLFloaterPerms::getEveryonePerms(), + name, NULL, + LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), NULL); + + LLFile::remove(filename); + + mTransactionMap[uuid] = mesh; + + LLFloaterReg::findTypedInstance("import_collada")->setStatusAssetUploading(name); + + return TRUE; +} + + +// called by the mesh asset upload responder to indicate the mesh asset has been uploaded +void LLImportColladaAssetCache::assetUploaded(LLUUID transaction_uuid, LLUUID asset_uuid, BOOL success) +{ + std::map::iterator i = mTransactionMap.find(transaction_uuid); + + if (i != mTransactionMap.end()) + { + daeElement* element = i->second; + + + if (success) + { + mAssetMap[element] = asset_uuid; + } + else // failure + { + // if failed, put back on end of queue + mUploadsPending.push_back(element); + } + + mUploads--; + uploadNextAsset(); + } +} + +const S32 MAX_CONCURRENT_UPLOADS = 5; + +void LLImportColladaAssetCache::uploadNextAsset() +{ + while ((mUploadsPending.size() > 0) && (mUploads < MAX_CONCURRENT_UPLOADS)) + { + BOOL upload_started = FALSE; + + daeElement* element = mUploadsPending.back(); + mUploadsPending.pop_back(); + + domImage* image = daeSafeCast(element); + if (image) + { + upload_started = uploadImageAsset(image); + } + + domMesh* mesh = daeSafeCast(element); + if (mesh) + { + upload_started = uploadMeshAsset(mesh); + } + + if (upload_started) + { + mUploads++; + } + + } + + if ((mUploadsPending.size() == 0) && (mUploads == 0)) + { + // we're done! notify the importer + LLImportCollada::getInstance()->assetsUploaded(); + } + + updateCount(); +} + + +void LLImportColladaAssetCache::clear() +{ + mDAE = NULL; + mTransactionMap.clear(); + mAssetMap.clear(); + mUploadsPending.clear(); + mUploads = 0; +} + +void LLImportColladaAssetCache::endImport() +{ + clear(); +} + +void LLImportColladaAssetCache::updateCount() +{ + S32 mesh_count = 0; + S32 image_count = 0; + + for (S32 i = 0; i < mUploadsPending.size(); i++) + { + daeElement* element = mUploadsPending[i]; + + if (daeSafeCast(element)) + { + mesh_count++; + } + + if (daeSafeCast(element)) + { + image_count++; + } + } + + LLFloaterReg::findTypedInstance("import_collada")->setAssetCount(mesh_count, image_count); +} + +void LLImportColladaAssetCache::prepareForUpload(DAE* dae) +{ + clear(); + mDAE = dae; + + daeDatabase* db = mDAE->getDatabase(); + + S32 mesh_count = db->getElementCount(NULL, COLLADA_TYPE_MESH); + for (S32 i = 0; i < mesh_count; i++) + { + domMesh* mesh = NULL; + + db->getElement((daeElement**) &mesh, i, NULL, COLLADA_TYPE_MESH); + + mUploadsPending.push_back(mesh); + } + + + S32 image_count = db->getElementCount(NULL, COLLADA_TYPE_IMAGE); + for (S32 i = 0; i < image_count; i++) + { + domImage* image = NULL; + db->getElement((daeElement**) &image, i, NULL, COLLADA_TYPE_IMAGE); + + mUploadsPending.push_back(image); + } + + updateCount(); +} + + +void LLImportColladaAssetCache::uploadAssets() +{ + uploadNextAsset(); +} + + +LLUUID LLImportColladaAssetCache::getAssetForDaeElement(daeElement* element) +{ + LLUUID id; + + std::map::iterator i = mAssetMap.find(element); + if (i != mAssetMap.end()) + { + id = i->second; + } + + return id; +} + + +// +// importer +// + + + +LLImportCollada::LLImportCollada() +{ + mIsImporting = FALSE; +} + + + + +void LLImportCollada::appendObjectAsset(domInstance_geometry* instance_geo) +{ + domGeometry* geo = daeSafeCast(instance_geo->getUrl().getElement()); + if (!geo) + { + llwarns << "cannot find geometry" << llendl; + return; + } + + domMesh* mesh = daeSafeCast(geo->getDescendant(daeElement::matchType(domMesh::ID()))); + if (!mesh) + { + llwarns << "could not find mesh" << llendl; + return; + } + + LLUUID mesh_asset = LLImportColladaAssetCache::getInstance()->getAssetForDaeElement(mesh); + if (mesh_asset.isNull()) + { + llwarns << "no mesh asset, skipping" << llendl; + return; + } + + // load the model + LLModel* model = LLModel::loadModelFromDomMesh(mesh); + + + + + // get our local transformation + LLMatrix4 transformation = mStack.front().transformation; + + // adjust the transformation to compensate for mesh normalization + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + transformation = matrix_multiply(mesh_translation, transformation); + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + transformation = matrix_multiply(mesh_scale, transformation); + + // check for reflection + BOOL reflected = (transformation.determinant() < 0); + + // compute position + LLVector3 position = LLVector3(0, 0, 0) * transformation; + + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); + + // adjust for "reflected" geometry + LLVector3 x_transformed_reflected = x_transformed; + if (reflected) + { + x_transformed_reflected *= -1.0; + } + + // compute rotation + LLMatrix3 rotation_matrix; + rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed); + LLQuaternion quat_rotation = rotation_matrix.quaternion(); + quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here. + LLVector3 euler_rotation; + quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]); + + + // + // build parameter block to construct this prim + // + + LLSD object_params; + + // create prim + + // set volume params + LLVolumeParams volume_params; + volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE ); + volume_params.setBeginAndEndS( 0.f, 1.f ); + volume_params.setBeginAndEndT( 0.f, 1.f ); + volume_params.setRatio ( 1, 1 ); + volume_params.setShear ( 0, 0 ); + U8 sculpt_type = LL_SCULPT_TYPE_MESH; + if (reflected) + { + sculpt_type |= LL_SCULPT_FLAG_MIRROR; + } + volume_params.setSculptID(mesh_asset, sculpt_type); + object_params["shape"] = volume_params.asLLSD(); + + object_params["material"] = LL_MCODE_WOOD; + + object_params["group-id"] = gAgent.getGroupID(); + object_params["pos"] = ll_sd_from_vector3(position); + object_params["rotation"] = ll_sd_from_quaternion(quat_rotation); + object_params["scale"] = ll_sd_from_vector3(scale); + object_params["name"] = mStack.front().name; + + // load material from dae file + std::vector texture_entries = getMaterials(model, instance_geo); + object_params["facelist"] = LLSD::emptyArray(); + for (int i = 0; i < texture_entries.size(); i++) + { + object_params["facelist"][i] = texture_entries[i].asLLSD(); + } + + // set extra parameters + LLSculptParams sculpt_params; + sculpt_params.setSculptTexture(mesh_asset); + sculpt_params.setSculptType(sculpt_type); + U8 buffer[MAX_OBJECT_PARAMS_SIZE+1]; + LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE); + sculpt_params.pack(dp); + std::vector v(dp.getCurrentSize()); + memcpy(&v[0], buffer, dp.getCurrentSize()); + LLSD extra_parameter; + extra_parameter["extra_parameter"] = sculpt_params.mType; + extra_parameter["param_data"] = v; + object_params["extra_parameters"].append(extra_parameter); + + mObjectList.append(object_params); + + delete model; + + LLFloaterReg::findTypedInstance("import_collada")->setStatusCreatingPrim(mStack.front().name); + + return; +} + +void LLImportCollada::uploadObjectAsset() +{ + LLSD request; + request["objects"] = mObjectList; + + std::string url = gAgent.getRegion()->getCapability("UploadObjectAsset"); + LLHTTPClient::post(url, request, new LLHTTPClient::Responder()); +} + + + +void LLImportCollada::importFile(std::string filename) +{ + if (mIsImporting) + { + llwarns << "Importer already running, import command for " << filename << " ignored" << llendl; + return; + } + + LLFloaterReg::showInstance("import_collada"); + LLFloaterReg::findTypedInstance("import_collada")->enableOK(TRUE); + + + mIsImporting = TRUE; + mDAE = new DAE; + mImportOrigin = gAgent.getPositionAgent() + LLVector3(0, 0, 2); + mSceneTransformation = LLMatrix4(); // identity + mFilename = filename; + mCreates = 0; + mObjectList = LLSD::emptyArray(); + + if (mDAE->open(mFilename) == NULL) + { + llwarns << "cannot open file " << mFilename << llendl; + endImport(); + return; + } + + LLImportColladaAssetCache::getInstance()->prepareForUpload(mDAE); + + return; +} + + +void LLImportCollada::assetsUploaded() +{ + if (!mIsImporting) + { + // weird, we got a callback while not importing. + return; + } + + daeDocument* doc = mDAE->getDoc(mFilename); + if (!doc) + { + llwarns << "can't find internal doc" << llendl; + endImport(); + } + + daeElement* root = doc->getDomRoot(); + if (!root) + { + llwarns << "document has no root" << llendl; + endImport(); + } + + domAsset::domUnit* unit = daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID()))); + if (unit) + { + mSceneTransformation *= unit->getMeter(); + } + + domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP + domAsset::domUp_axis* up_axis = + daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID()))); + if (up_axis) + { + up = up_axis->getValue(); + } + + if (up == UPAXISTYPE_X_UP) + { + LLMatrix4 rotation; + rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); + + mSceneTransformation = matrix_multiply(rotation, mSceneTransformation); + } + else if (up == UPAXISTYPE_Y_UP) + { + LLMatrix4 rotation; + rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); + + mSceneTransformation = matrix_multiply(rotation, mSceneTransformation); + } + // else Z_UP, which is our behavior + + + + daeElement* scene = root->getDescendant("visual_scene"); + if (!scene) + { + llwarns << "document has no visual_scene" << llendl; + endImport(); + } + + processElement(scene); + processNextElement(); +} + +void LLImportCollada::pushStack(daeElement* next_element, std::string name, LLMatrix4 transformation) +{ + struct StackState new_state; + + new_state.next_element = next_element; + new_state.name = name; + new_state.transformation = transformation; + + mStack.push_front(new_state); +} + + + +void LLImportCollada::popStack() +{ + mStack.pop_front(); +} + + + +BOOL LLImportCollada::processElement(daeElement* element) +{ + if (mStack.size() > 0) + { + mStack.front().next_element = getNextSibling(element); + } + + domTranslate* translate = daeSafeCast(element); + if (translate) + { + domFloat3 dom_value = translate->getValue(); + + LLMatrix4 translation; + translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + + mStack.front().transformation = matrix_multiply(translation, mStack.front().transformation); + } + + domRotate* rotate = daeSafeCast(element); + if (rotate) + { + domFloat4 dom_value = rotate->getValue(); + + LLMatrix4 rotation; + rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0)); + + mStack.front().transformation = matrix_multiply(rotation, mStack.front().transformation); + } + + domScale* scale = daeSafeCast(element); + if (scale) + { + domFloat3 dom_value = scale->getValue(); + + LLMatrix4 scaling; + scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + + mStack.front().transformation = matrix_multiply(scaling, mStack.front().transformation); + } + + domMatrix* matrix = daeSafeCast(element); + if (matrix) + { + domFloat4x4 dom_value = matrix->getValue(); + + LLMatrix4 matrix_transform; + + for (int i = 0; i < 4; i++) + for(int j = 0; j < 4; j++) + { + matrix_transform.mMatrix[i][j] = dom_value[i + j*4]; + } + + mStack.front().transformation = matrix_multiply(matrix_transform, mStack.front().transformation); + } + + domInstance_geometry* instance_geo = daeSafeCast(element); + if (instance_geo) + { + appendObjectAsset(instance_geo); + } + + domNode* node = daeSafeCast(element); + if (node) + { + pushStack(getFirstChild(element), getElementLabel(element), mStack.front().transformation); + } + + domInstance_node* instance_node = daeSafeCast(element); + if (instance_node) + { + daeElement* instance = instance_node->getUrl().getElement(); + if (instance) + { + pushStack(getFirstChild(instance), getElementLabel(instance), mStack.front().transformation); + } + } + + domVisual_scene* scene = daeSafeCast(element); + if (scene) + { + pushStack(getFirstChild(element), std::string("scene"), mSceneTransformation); + } + + return FALSE; +} + + +void LLImportCollada::processNextElement() +{ + while(1) + { + if (mStack.size() == 0) + { + uploadObjectAsset(); + endImport(); + return; + } + + daeElement *element = mStack.front().next_element; + + if (element == NULL) + { + popStack(); + } + else + { + processElement(element); + } + } +} + + +void LLImportCollada::endImport() +{ + LLFloaterReg::hideInstance("import_collada"); + + LLImportColladaAssetCache::getInstance()->endImport(); + + if (mDAE) + { + delete mDAE; + mDAE = NULL; + } + + mIsImporting = FALSE; +} + + +/* static */ +void LLImportCollada::onCommitOK(LLUICtrl*, void*) +{ + LLFloaterReg::findTypedInstance("import_collada")->enableOK(FALSE); + + LLImportColladaAssetCache::getInstance()->uploadAssets(); +} + + +/* static */ +void LLImportCollada::onCommitCancel(LLUICtrl*, void*) +{ + getInstance()->endImport(); +} + +#endif diff --git a/indra/newview/llfloaterimportcollada.h b/indra/newview/llfloaterimportcollada.h new file mode 100644 index 0000000000..818b19e403 --- /dev/null +++ b/indra/newview/llfloaterimportcollada.h @@ -0,0 +1,143 @@ +/** + * @file llfloaterimportcollada.h + * @brief The about box from Help -> About + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERIMPORTCOLLADA_H +#define LL_LLFLOATERIMPORTCOLLADA_H + +#include "llfloater.h" +#include "llvolume.h" //for LL_MESH_ENABLED + +#if LL_MESH_ENABLED + +class LLFloaterImportCollada : public LLFloater +{ +public: + LLFloaterImportCollada(const LLSD& key); + /* virtual */ BOOL postBuild(); + + void setAssetCount(S32 mesh_count, S32 texture_count); + void setStatusAssetUploading(std::string asset_name); + void setStatusCreatingPrim(std::string prim_name); + void setStatusIdle(); + void enableOK(BOOL enable); +}; + +class LLViewerObject; +class DAE; +class daeElement; +class domMesh; +class domImage; +class domInstance_geometry; +class LLModel; +class LLImportCollada; + +class LLImportColladaAssetCache : public LLSingleton +{ +public: + // called first to initialize + void prepareForUpload(DAE* dae); + + // upload the assets in this collada file + void uploadAssets(); + + // get the uploaded assets which corresponds to this element + LLUUID getAssetForDaeElement(daeElement* element); + + // stop the upload + void endImport(); + + // reset + void clear(); + + // callback for notification when an asset has been uploaded + void assetUploaded(LLUUID transaction_uuid, LLUUID asset_uuid, BOOL success); + +private: + void uploadNextAsset(); + BOOL uploadMeshAsset(domMesh* mesh); + BOOL uploadImageAsset(domImage* image); + void updateCount(); + + std::vector mUploadsPending; + std::map mTransactionMap; + std::map mAssetMap; + + DAE* mDAE; + S32 mUploads; +}; + + +class LLImportCollada +: public LLSingleton +{ +public: + LLImportCollada(); + void importFile(std::string filename); + + // callback when all assets have been uploaded + void assetsUploaded(); + + // callback when buttons pressed + static void onCommitOK(LLUICtrl*, void*); + static void onCommitCancel(LLUICtrl*, void*); + + +private: + void endImport(); + void processNextElement(); + BOOL processElement(daeElement* element); + void pushStack(daeElement* next_element, std::string name, LLMatrix4 transformation); + void popStack(); + void appendObjectAsset(domInstance_geometry* instance_geo); + void uploadObjectAsset(); + + struct StackState + { + daeElement* next_element; + std::string name; + LLMatrix4 transformation; + }; + + std::list mStack; + S32 mCreates; + LLVector3 mImportOrigin; + std::string mFilename; + BOOL mIsImporting; + DAE *mDAE; + LLSD mObjectList; + + LLMatrix4 mSceneTransformation; +}; + +#endif + +#endif // LL_LLFLOATERIMPORTCOLLADA_H diff --git a/indra/newview/llfloatermodelpreview.cpp b/indra/newview/llfloatermodelpreview.cpp new file mode 100644 index 0000000000..5dd983d818 --- /dev/null +++ b/indra/newview/llfloatermodelpreview.cpp @@ -0,0 +1,3382 @@ +/** + * @file llfloatermodelpreview.cpp + * @brief LLFloaterModelPreview class implementation + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "dae.h" +//#include "dom.h" +#include "dom/domAsset.h" +#include "dom/domBind_material.h" +#include "dom/domCOLLADA.h" +#include "dom/domConstants.h" +#include "dom/domController.h" +#include "dom/domEffect.h" +#include "dom/domGeometry.h" +#include "dom/domInstance_geometry.h" +#include "dom/domInstance_material.h" +#include "dom/domInstance_node.h" +#include "dom/domInstance_effect.h" +#include "dom/domMaterial.h" +#include "dom/domMatrix.h" +#include "dom/domNode.h" +#include "dom/domProfile_COMMON.h" +#include "dom/domRotate.h" +#include "dom/domScale.h" +#include "dom/domTranslate.h" +#include "dom/domVisual_scene.h" + +#include "llfloatermodelpreview.h" + +#include "llfilepicker.h" +#include "llimagebmp.h" +#include "llimagetga.h" +#include "llimagejpeg.h" +#include "llimagepng.h" + +#include "llagent.h" +#include "llbutton.h" +#include "llcombobox.h" +#include "lldatapacker.h" +#include "lldrawable.h" +#include "lldrawpoolavatar.h" +#include "llrender.h" +#include "llface.h" +#include "lleconomy.h" +#include "llfocusmgr.h" +#include "llfloaterperms.h" +#include "llmatrix4a.h" +#include "llmeshrepository.h" +#include "llsdutil_math.h" +#include "lltextbox.h" +#include "lltoolmgr.h" +#include "llui.h" +#include "llvector4a.h" +#include "llviewercamera.h" +#include "llviewerwindow.h" +#include "llvoavatar.h" +#include "llvoavatarself.h" +#include "pipeline.h" +#include "lluictrlfactory.h" +#include "llviewermenufile.h" +#include "llviewerregion.h" +#include "llstring.h" +#include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llsliderctrl.h" +#include "llspinctrl.h" +#include "llvfile.h" +#include "llvfs.h" + + +#include "glod/glod.h" + + +#if LL_MESH_ENABLED + +//static +S32 LLFloaterModelPreview::sUploadAmount = 10; +LLFloaterModelPreview* LLFloaterModelPreview::sInstance = NULL; + +const S32 PREVIEW_BORDER_WIDTH = 2; +const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH; +const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE; +const S32 PREF_BUTTON_HEIGHT = 16 + 7 + 16; +const S32 PREVIEW_TEXTURE_HEIGHT = 300; + +void drawBoxOutline(const LLVector3& pos, const LLVector3& size); + +std::string limit_name[] = +{ + "lowest limit", + "low limit", + "medium limit", + "high limit", + "physics limit", + + "I went off the end of the limit_name array. Me so smart." +}; + +std::string info_name[] = +{ + "lowest info", + "low info", + "medium info", + "high info", + "physics info", + + "I went off the end of the info_name array. Me so smart." +}; + +bool validate_face(const LLVolumeFace& face) +{ + for (U32 i = 0; i < face.mNumIndices; ++i) + { + if (face.mIndices[i] >= face.mNumVertices) + { + llwarns << "Face has invalid index." << llendl; + return false; + } + } + + return true; +} + +bool validate_model(const LLModel* mdl) +{ + if (mdl->getNumVolumeFaces() == 0) + { + llwarns << "Model has no faces!" << llendl; + return false; + } + + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + if (mdl->getVolumeFace(i).mNumVertices == 0) + { + llwarns << "Face has no vertices." << llendl; + return false; + } + + if (mdl->getVolumeFace(i).mNumIndices == 0) + { + llwarns << "Face has no indices." << llendl; + return false; + } + + if (!validate_face(mdl->getVolumeFace(i))) + { + return false; + } + } + + return true; +} + +BOOL stop_gloderror() +{ + GLuint error = glodGetError(); + + if (error != GLOD_NO_ERROR) + { + llwarns << "GLOD error detected, cannot generate LOD: " << std::hex << error << llendl; + return TRUE; + } + + return FALSE; +} + +LLPhysicsDecompFloater::LLPhysicsDecompFloater(LLSD& key) +: LLFloater(key) +{ + +} + +LLPhysicsDecompFloater::~LLPhysicsDecompFloater() +{ + if (LLFloaterModelPreview::sInstance && LLFloaterModelPreview::sInstance->mDecompFloater) + { + LLFloaterModelPreview::sInstance->mDecompFloater = NULL; + } +} + +class LLMeshFilePicker : public LLFilePickerThread +{ +public: + LLFloaterModelPreview* mFMP; + S32 mLOD; + + LLMeshFilePicker(LLFloaterModelPreview* fmp, S32 lod) + : LLFilePickerThread(LLFilePicker::FFLOAD_COLLADA) + { + mFMP = fmp; + mLOD = lod; + } + + virtual void notify(const std::string& filename) + { + mFMP->mModelPreview->loadModel(mFile, mLOD); + } +}; + + +//----------------------------------------------------------------------------- +// LLFloaterModelPreview() +//----------------------------------------------------------------------------- +LLFloaterModelPreview::LLFloaterModelPreview(const LLSD& key) : + LLFloater(key) +{ + sInstance = this; + mLastMouseX = 0; + mLastMouseY = 0; + mGLName = 0; + mLoading = FALSE; + mDecompFloater = NULL; +} + +//----------------------------------------------------------------------------- +// postBuild() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::postBuild() +{ + if (!LLFloater::postBuild()) + { + return FALSE; + } + + childSetCommitCallback("high detail combo", onHighLODCommit, this); + childSetCommitCallback("medium detail combo", onMediumLODCommit, this); + childSetCommitCallback("low detail combo", onLowLODCommit, this); + childSetCommitCallback("lowest detail combo", onLowestLODCommit, this); + childSetCommitCallback("physics detail combo", onPhysicsLODCommit, this); + + + childSetCommitCallback("high limit", onHighLimitCommit, this); + childSetCommitCallback("medium limit", onMediumLimitCommit, this); + childSetCommitCallback("low limit", onLowLimitCommit, this); + childSetCommitCallback("lowest limit", onLowestLimitCommit, this); + childSetCommitCallback("physics limit", onPhysicsLimitCommit, this); + + childSetCommitCallback("smooth normals", onSmoothNormalsCommit, this); + + childSetCommitCallback("show edges", onShowEdgesCommit, this); + childSetCommitCallback("auto fill", onAutoFillCommit, this); + + childSetCommitCallback("explode", onExplodeCommit, this); + + childSetTextArg("status", "[STATUS]", getString("status_idle")); + + for (S32 lod = 0; lod < LLModel::NUM_LODS; ++lod) + { + if (lod == LLModel::LOD_PHYSICS) + { + childSetTextArg(info_name[lod], "[HULLS]", std::string("0")); + childSetTextArg(info_name[lod], "[POINTS]", std::string("0")); + } + else + { + childSetTextArg(info_name[lod], "[TRIANGLES]", std::string("0")); + childSetTextArg(info_name[lod], "[VERTICES]", std::string("0")); + childSetTextArg(info_name[lod], "[SUBMESHES]", std::string("0")); + std::string msg = getString("required"); + childSetTextArg(info_name[lod], "[MESSAGE]", msg); + } + + childSetVisible(limit_name[lod], FALSE); + } + + //childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount)); + childSetAction("ok_btn", onUpload, this); + + childSetAction("consolidate", onConsolidate, this); + childSetAction("scrub materials", onScrubMaterials, this); + + childSetAction("decompose_btn", onDecompose, this); + + childSetCommitCallback("preview_lod_combo", onPreviewLODCommit, this); + + const U32 width = 512; + const U32 height = 512; + + mPreviewRect.set(getRect().getWidth()-PREVIEW_HPAD-width, + PREVIEW_HPAD+height, + getRect().getWidth()-PREVIEW_HPAD, + PREVIEW_HPAD); + + mModelPreview = new LLModelPreview(512, 512, this); + mModelPreview->setPreviewTarget(16.f); + + return TRUE; +} + +//----------------------------------------------------------------------------- +// LLFloaterModelPreview() +//----------------------------------------------------------------------------- +LLFloaterModelPreview::~LLFloaterModelPreview() +{ + sInstance = NULL; + + delete mModelPreview; + + if (mGLName) + { + LLImageGL::deleteTextures(1, &mGLName ); + } + + if (mDecompFloater) + { + mDecompFloater->closeFloater(); + mDecompFloater = NULL; + } +} + +void LLFloaterModelPreview::loadModel(S32 lod) +{ + mLoading = TRUE; + + (new LLMeshFilePicker(this, lod))->getFile(); +} + +void LLFloaterModelPreview::setLODMode(S32 lod, S32 mode) +{ + if (mode == 0) + { + loadModel(lod); + } + else if (mode != mModelPreview->mLODMode[lod]) + { + mModelPreview->mLODMode[lod] = mode; + mModelPreview->genLODs(lod); + } + + mModelPreview->setPreviewLOD(lod); + + + LLSpinCtrl* lim = getChild(limit_name[lod], TRUE); + + if (mode == 1) //triangle count + { + U32 tri_count = 0; + for (LLModelLoader::model_list::iterator iter = mModelPreview->mBaseModel.begin(); + iter != mModelPreview->mBaseModel.end(); ++iter) + { + tri_count += (*iter)->getNumTriangles(); + } + + lim->setMaxValue(tri_count); + lim->setVisible(TRUE); + } + else + { + lim->setVisible(FALSE); + } +} + +void LLFloaterModelPreview::setLimit(S32 lod, S32 limit) +{ + if (limit != mModelPreview->mLimit[lod]) + { + mModelPreview->mLimit[lod] = limit; + mModelPreview->genLODs(lod); + mModelPreview->setPreviewLOD(lod); + } +} + +void LLFloaterModelPreview::onPreviewLODCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; + + if (!fp->mModelPreview) + { + return; + } + + S32 which_mode = 0; + + LLCtrlSelectionInterface* iface = fp->childGetSelectionInterface("preview_lod_combo"); + if (iface) + { + which_mode = iface->getFirstSelectedIndex(); + } + fp->mModelPreview->setPreviewLOD(which_mode); +} + +//static +void LLFloaterModelPreview::setLODMode(S32 lod, void* userdata) +{ + LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; + + if (!fp->mModelPreview) + { + return; + } + + S32 which_mode = 0; + + std::string combo_name[] = + { + "lowest detail combo", + "low detail combo", + "medium detail combo", + "high detail combo", + "physics detail combo", + + "I went off the end of the combo_name array. Me so smart." + }; + + LLCtrlSelectionInterface* iface = fp->childGetSelectionInterface(combo_name[lod]); + if (iface) + { + which_mode = iface->getFirstSelectedIndex(); + } + + fp->setLODMode(lod, which_mode); +} + +//static +void LLFloaterModelPreview::onHighLODCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLODMode(3, userdata); +} + +//static +void LLFloaterModelPreview::onMediumLODCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLODMode(2, userdata); +} + +//static +void LLFloaterModelPreview::onLowLODCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLODMode(1, userdata); +} + +//static +void LLFloaterModelPreview::onLowestLODCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLODMode(0, userdata); +} + +//static +void LLFloaterModelPreview::onPhysicsLODCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLODMode(4, userdata); +} + +//static +void LLFloaterModelPreview::setLimit(S32 lod, void* userdata) +{ + LLFloaterModelPreview *fp =(LLFloaterModelPreview *)userdata; + + if (!fp->mModelPreview) + { + return; + } + + S32 limit = fp->childGetValue(limit_name[lod]).asInteger(); + + + fp->setLimit(lod, limit); +} + +//static +void LLFloaterModelPreview::onHighLimitCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLimit(3, userdata); +} + +//static +void LLFloaterModelPreview::onMediumLimitCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLimit(2, userdata); +} + +//static +void LLFloaterModelPreview::onLowLimitCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLimit(1, userdata); +} + +//static +void LLFloaterModelPreview::onLowestLimitCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLimit(0, userdata); +} + +//static +void LLFloaterModelPreview::onPhysicsLimitCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview::setLimit(4, userdata); +} + +//static +void LLFloaterModelPreview::onSmoothNormalsCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; + + fp->mModelPreview->smoothNormals(); +} + +//static +void LLFloaterModelPreview::onShowEdgesCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; + + fp->mModelPreview->refresh(); +} + +//static +void LLFloaterModelPreview::onExplodeCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; + + fp->mModelPreview->refresh(); +} + +//static +void LLFloaterModelPreview::onAutoFillCommit(LLUICtrl* ctrl, void* userdata) +{ + LLFloaterModelPreview* fp = (LLFloaterModelPreview*) userdata; + + fp->mModelPreview->genLODs(); +} + + +//----------------------------------------------------------------------------- +// draw() +//----------------------------------------------------------------------------- +void LLFloaterModelPreview::draw() +{ + LLFloater::draw(); + LLRect r = getRect(); + + if (!mLoading) + { + childSetTextArg("status", "[STATUS]", getString("status_idle")); + } + + childSetTextArg("description_label", "[PRIM_COST]", llformat("%d", mModelPreview->mResourceCost)); + childSetTextArg("description_label", "[TEXTURES]", llformat("%d", mModelPreview->mTextureSet.size())); + + if (mDecompFloater) + { + mDecompFloater->childSetText("status", gMeshRepo.mDecompThread->mStatus); + } + + U32 resource_cost = mModelPreview->mResourceCost*10; + + if (childGetValue("upload_textures").asBoolean()) + { + resource_cost += mModelPreview->mTextureSet.size()*10; + } + + childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d", resource_cost)); + + if (mModelPreview) + { + gGL.color3f(1.f, 1.f, 1.f); + + gGL.getTexUnit(0)->bind(mModelPreview); + + gGL.begin( LLRender::QUADS ); + { + gGL.texCoord2f(0.f, 1.f); + gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mTop); + gGL.texCoord2f(0.f, 0.f); + gGL.vertex2i(mPreviewRect.mLeft, mPreviewRect.mBottom); + gGL.texCoord2f(1.f, 0.f); + gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mBottom); + gGL.texCoord2f(1.f, 1.f); + gGL.vertex2i(mPreviewRect.mRight, mPreviewRect.mTop); + } + gGL.end(); + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + } +} + +//----------------------------------------------------------------------------- +// handleMouseDown() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleMouseDown(S32 x, S32 y, MASK mask) +{ + if (mPreviewRect.pointInRect(x, y)) + { + bringToFront( x, y ); + gFocusMgr.setMouseCapture(this); + gViewerWindow->hideCursor(); + mLastMouseX = x; + mLastMouseY = y; + return TRUE; + } + + return LLFloater::handleMouseDown(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleMouseUp() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleMouseUp(S32 x, S32 y, MASK mask) +{ + gFocusMgr.setMouseCapture(FALSE); + gViewerWindow->showCursor(); + return LLFloater::handleMouseUp(x, y, mask); +} + +//----------------------------------------------------------------------------- +// handleHover() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleHover (S32 x, S32 y, MASK mask) +{ + MASK local_mask = mask & ~MASK_ALT; + + if (mModelPreview && hasMouseCapture()) + { + if (local_mask == MASK_PAN) + { + // pan here + mModelPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f); + } + else if (local_mask == MASK_ORBIT) + { + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f; + + mModelPreview->rotate(yaw_radians, pitch_radians); + } + else + { + + F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f; + F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f; + + mModelPreview->rotate(yaw_radians, 0.f); + mModelPreview->zoom(zoom_amt); + } + + + mModelPreview->refresh(); + + LLUI::setMousePositionLocal(this, mLastMouseX, mLastMouseY); + } + + if (!mPreviewRect.pointInRect(x, y) || !mModelPreview) + { + return LLFloater::handleHover(x, y, mask); + } + else if (local_mask == MASK_ORBIT) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA); + } + else if (local_mask == MASK_PAN) + { + gViewerWindow->setCursor(UI_CURSOR_TOOLPAN); + } + else + { + gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN); + } + + return TRUE; +} + +//----------------------------------------------------------------------------- +// handleScrollWheel() +//----------------------------------------------------------------------------- +BOOL LLFloaterModelPreview::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + if (mPreviewRect.pointInRect(x, y) && mModelPreview) + { + mModelPreview->zoom((F32)clicks * -0.2f); + mModelPreview->refresh(); + } + + return TRUE; +} + +//static +void LLFloaterModelPreview::onPhysicsParamCommit(LLUICtrl* ctrl, void* data) +{ + LLCDParam* param = (LLCDParam*) data; + + LLCDResult ret = LLCD_OK; + + if (LLConvexDecomposition::getInstance() == NULL) + { + llinfos << "convex decomposition tool is a stub on this platform. cannot get decomp." << llendl; + return; + } + + if (param->mType == LLCDParam::LLCD_FLOAT) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, (F32) ctrl->getValue().asReal()); + } + else if (param->mType == LLCDParam::LLCD_INTEGER || + param->mType == LLCDParam::LLCD_ENUM) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, ctrl->getValue().asInteger()); + } + else if (param->mType == LLCDParam::LLCD_BOOLEAN) + { + ret = LLConvexDecomposition::getInstance()->setParam(param->mName, ctrl->getValue().asBoolean()); + } + + if (ret) + { + llerrs << "WTF?" << llendl; + } +} + +//static +void LLFloaterModelPreview::onPhysicsStageExecute(LLUICtrl* ctrl, void* data) +{ + LLCDStageData* stage = (LLCDStageData*) data; + + LLModel* mdl = NULL; + + if (sInstance) + { + if (sInstance->mModelPreview) + { + if (sInstance->mDecompFloater) + { + S32 idx = sInstance->mDecompFloater->childGetValue("model").asInteger(); + if (idx >= 0 && idx < sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS].size()) + { + mdl = sInstance->mModelPreview->mModel[LLModel::LOD_PHYSICS][idx]; + } + } + } + } + + if (mdl) + { + gMeshRepo.mDecompThread->execute(stage->mName, mdl); + } +} + +//static +void LLFloaterModelPreview::onPhysicsStageCancel(LLUICtrl* ctrl, void*data) +{ + gMeshRepo.mDecompThread->cancel(); +} + +void LLFloaterModelPreview::showDecompFloater() +{ + if (!mDecompFloater) + { + LLSD key; + mDecompFloater = new LLPhysicsDecompFloater(key); + + S32 left = 20; + S32 right = 270; + + S32 cur_y = 30; + + { + //add status text + LLTextBox::Params p; + p.name("status"); + p.rect(LLRect(left, cur_y, right-80, cur_y-20)); + mDecompFloater->addChild(LLUICtrlFactory::create(p)); + } + + + { //add cancel button + LLButton::Params p; + p.name("Cancel"); + p.label("Cancel"); + p.rect(LLRect(right-80, cur_y, right, cur_y-20)); + LLButton* button = LLUICtrlFactory::create(p); + button->setCommitCallback(onPhysicsStageCancel, NULL); + mDecompFloater->addChild(button); + } + + cur_y += 30; + + + const LLCDStageData* stage; + S32 stage_count = 0; + if (LLConvexDecomposition::getInstance() != NULL) + { + stage_count = LLConvexDecomposition::getInstance()->getStages(&stage); + } + + const LLCDParam* param; + S32 param_count = 0; + if (LLConvexDecomposition::getInstance() != NULL) + { + param_count = LLConvexDecomposition::getInstance()->getParameters(¶m); + } + + for (S32 j = stage_count-1; j >= 0; --j) + { + LLButton::Params p; + p.name(stage[j].mName); + p.label(stage[j].mName); + p.rect(LLRect(left, cur_y, right, cur_y-20)); + LLButton* button = LLUICtrlFactory::create(p); + button->setCommitCallback(onPhysicsStageExecute, (void*) &stage[j]); + mDecompFloater->addChild(button); + gMeshRepo.mDecompThread->mStageID[stage[j].mName] = j; + cur_y += 30; + // protected against stub by stage_count being 0 for stub above + LLConvexDecomposition::getInstance()->registerCallback(j, LLPhysicsDecomp::llcdCallback); + + for (S32 i = 0; i < param_count; ++i) + { + if (param[i].mStage != j) + { + continue; + } + + if (param[i].mType == LLCDParam::LLCD_FLOAT) + { + LLSliderCtrl::Params p; + p.name(param[i].mName); + p.label(param[i].mName); + p.rect(LLRect(left, cur_y, right, cur_y-20)); + p.min_value(param[i].mDetails.mRange.mLow.mFloat); + p.max_value(param[i].mDetails.mRange.mHigh.mFloat); + p.increment(param[i].mDetails.mRange.mDelta.mFloat); + p.decimal_digits(3); + p.initial_value(param[i].mDefault.mFloat); + LLSliderCtrl* slider = LLUICtrlFactory::create(p); + slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + mDecompFloater->addChild(slider); + cur_y += 30; + } + else if (param[i].mType == LLCDParam::LLCD_INTEGER) + { + LLSliderCtrl::Params p; + p.name(param[i].mName); + p.label(param[i].mName); + p.rect(LLRect(left, cur_y, right, cur_y-20)); + p.min_value(param[i].mDetails.mRange.mLow.mIntOrEnumValue); + p.max_value(param[i].mDetails.mRange.mHigh.mIntOrEnumValue); + p.increment(param[i].mDetails.mRange.mDelta.mIntOrEnumValue); + p.initial_value(param[i].mDefault.mIntOrEnumValue); + LLSliderCtrl* slider = LLUICtrlFactory::create(p); + slider->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + mDecompFloater->addChild(slider); + cur_y += 30; + } + else if (param[i].mType == LLCDParam::LLCD_BOOLEAN) + { + LLCheckBoxCtrl::Params p; + p.rect(LLRect(left, cur_y, right, cur_y-20)); + p.name(param[i].mName); + p.label(param[i].mName); + p.initial_value(param[i].mDefault.mBool); + LLCheckBoxCtrl* check_box = LLUICtrlFactory::create(p); + check_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + mDecompFloater->addChild(check_box); + cur_y += 30; + } + else if (param[i].mType == LLCDParam::LLCD_ENUM) + { + LLComboBox::Params p; + p.rect(LLRect(left, cur_y, right/3, cur_y-20)); + p.name(param[i].mName); + p.label(param[i].mName); + LLComboBox* combo_box = LLUICtrlFactory::create(p); + for (S32 k = 0; k < param[i].mDetails.mEnumValues.mNumEnums; ++k) + { + combo_box->add(param[i].mDetails.mEnumValues.mEnumsArray[k].mName, + LLSD::Integer(param[i].mDetails.mEnumValues.mEnumsArray[k].mValue)); + } + combo_box->setValue(param[i].mDefault.mIntOrEnumValue); + combo_box->setCommitCallback(onPhysicsParamCommit, (void*) ¶m[i]); + mDecompFloater->addChild(combo_box); + cur_y += 30; + } + } + } + + //mesh render checkbox + { + LLCheckBoxCtrl::Params p; + p.label("Mesh: "); + p.name("render_mesh"); + p.rect(LLRect(left, cur_y, right/4, cur_y-20)); + LLCheckBoxCtrl* check = LLUICtrlFactory::create(p); + check->setValue(true); + mDecompFloater->addChild(check); + } + + //hull render checkbox + { + LLCheckBoxCtrl::Params p; + p.label("Hull: "); + p.name("render_hull"); + p.rect(LLRect(right/4, cur_y, right/2, cur_y-20)); + LLCheckBoxCtrl* check = LLUICtrlFactory::create(p); + check->setValue(true); + mDecompFloater->addChild(check); + } + + { //submesh combo box label + LLTextBox::Params p; + p.label("Model"); + p.name("model label"); + p.rect(LLRect(right/3, cur_y, right/2, cur_y-20)); + LLTextBox* text_box = LLUICtrlFactory::create(p); + text_box->setValue("Model"); + mDecompFloater->addChild(text_box); + } + { + //add submesh combo box + LLComboBox::Params p; + p.rect(LLRect(right/2, cur_y, right, cur_y-20)); + p.name("model"); + LLComboBox* combo_box = LLUICtrlFactory::create(p); + for (S32 i = 0; i < mModelPreview->mBaseModel.size(); ++i) + { + LLModel* mdl = mModelPreview->mBaseModel[i]; + combo_box->add(mdl->mLabel, i); + } + combo_box->setValue(0); + mDecompFloater->addChild(combo_box); + cur_y += 30; + } + + mDecompFloater->childSetCommitCallback("model", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance); + mDecompFloater->childSetCommitCallback("render_mesh", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance); + mDecompFloater->childSetCommitCallback("render_hull", LLFloaterModelPreview::refresh, LLFloaterModelPreview::sInstance); + + mDecompFloater->setRect(LLRect(10, cur_y+20, right+20, 10)); + } + + mDecompFloater->openFloater(); +} + +//----------------------------------------------------------------------------- +// onMouseCaptureLost() +//----------------------------------------------------------------------------- +// static +void LLFloaterModelPreview::onMouseCaptureLostModelPreview(LLMouseHandler* handler) +{ + gViewerWindow->showCursor(); +} + +//----------------------------------------------------------------------------- +// LLModelLoader +//----------------------------------------------------------------------------- +LLModelLoader::LLModelLoader(std::string filename, S32 lod, LLModelPreview* preview) +: LLThread("Model Loader"), mFilename(filename), mLod(lod), mPreview(preview), mState(STARTING), mFirstTransform(TRUE) +{ + mJointMap["mPelvis"] = "mPelvis"; + mJointMap["mTorso"] = "mTorso"; + mJointMap["mChest"] = "mChest"; + mJointMap["mNeck"] = "mNeck"; + mJointMap["mHead"] = "mHead"; + mJointMap["mSkull"] = "mSkull"; + mJointMap["mEyeRight"] = "mEyeRight"; + mJointMap["mEyeLeft"] = "mEyeLeft"; + mJointMap["mCollarLeft"] = "mCollarLeft"; + mJointMap["mShoulderLeft"] = "mShoulderLeft"; + mJointMap["mElbowLeft"] = "mElbowLeft"; + mJointMap["mWristLeft"] = "mWristLeft"; + mJointMap["mCollarRight"] = "mCollarRight"; + mJointMap["mShoulderRight"] = "mShoulderRight"; + mJointMap["mElbowRight"] = "mElbowRight"; + mJointMap["mWristRight"] = "mWristRight"; + mJointMap["mHipRight"] = "mHipRight"; + mJointMap["mKneeRight"] = "mKneeRight"; + mJointMap["mAnkleRight"] = "mAnkleRight"; + mJointMap["mFootRight"] = "mFootRight"; + mJointMap["mToeRight"] = "mToeRight"; + mJointMap["mHipLeft"] = "mHipLeft"; + mJointMap["mKneeLeft"] = "mKneeLeft"; + mJointMap["mAnkleLeft"] = "mAnkleLeft"; + mJointMap["mFootLeft"] = "mFootLeft"; + mJointMap["mToeLeft"] = "mToeLeft"; + + mJointMap["avatar_mPelvis"] = "mPelvis"; + mJointMap["avatar_mTorso"] = "mTorso"; + mJointMap["avatar_mChest"] = "mChest"; + mJointMap["avatar_mNeck"] = "mNeck"; + mJointMap["avatar_mHead"] = "mHead"; + mJointMap["avatar_mSkull"] = "mSkull"; + mJointMap["avatar_mEyeRight"] = "mEyeRight"; + mJointMap["avatar_mEyeLeft"] = "mEyeLeft"; + mJointMap["avatar_mCollarLeft"] = "mCollarLeft"; + mJointMap["avatar_mShoulderLeft"] = "mShoulderLeft"; + mJointMap["avatar_mElbowLeft"] = "mElbowLeft"; + mJointMap["avatar_mWristLeft"] = "mWristLeft"; + mJointMap["avatar_mCollarRight"] = "mCollarRight"; + mJointMap["avatar_mShoulderRight"] = "mShoulderRight"; + mJointMap["avatar_mElbowRight"] = "mElbowRight"; + mJointMap["avatar_mWristRight"] = "mWristRight"; + mJointMap["avatar_mHipRight"] = "mHipRight"; + mJointMap["avatar_mKneeRight"] = "mKneeRight"; + mJointMap["avatar_mAnkleRight"] = "mAnkleRight"; + mJointMap["avatar_mFootRight"] = "mFootRight"; + mJointMap["avatar_mToeRight"] = "mToeRight"; + mJointMap["avatar_mHipLeft"] = "mHipLeft"; + mJointMap["avatar_mKneeLeft"] = "mKneeLeft"; + mJointMap["avatar_mAnkleLeft"] = "mAnkleLeft"; + mJointMap["avatar_mFootLeft"] = "mFootLeft"; + mJointMap["avatar_mToeLeft"] = "mToeLeft"; + + + mJointMap["hip"] = "mPelvis"; + mJointMap["abdomen"] = "mTorso"; + mJointMap["chest"] = "mChest"; + mJointMap["neck"] = "mNeck"; + mJointMap["head"] = "mHead"; + mJointMap["figureHair"] = "mSkull"; + mJointMap["lCollar"] = "mCollarLeft"; + mJointMap["lShldr"] = "mShoulderLeft"; + mJointMap["lForeArm"] = "mElbowLeft"; + mJointMap["lHand"] = "mWristLeft"; + mJointMap["rCollar"] = "mCollarRight"; + mJointMap["rShldr"] = "mShoulderRight"; + mJointMap["rForeArm"] = "mElbowRight"; + mJointMap["rHand"] = "mWristRight"; + mJointMap["rThigh"] = "mHipRight"; + mJointMap["rShin"] = "mKneeRight"; + mJointMap["rFoot"] = "mFootRight"; + mJointMap["lThigh"] = "mHipLeft"; + mJointMap["lShin"] = "mKneeLeft"; + mJointMap["lFoot"] = "mFootLeft"; +} + +void stretch_extents(LLModel* model, LLMatrix4a& mat, LLVector4a& min, LLVector4a& max, BOOL& first_transform) +{ + LLVector4a box[] = + { + LLVector4a(-1, 1,-1), + LLVector4a(-1, 1, 1), + LLVector4a(-1,-1,-1), + LLVector4a(-1,-1, 1), + LLVector4a( 1, 1,-1), + LLVector4a( 1, 1, 1), + LLVector4a( 1,-1,-1), + LLVector4a( 1,-1, 1), + }; + + for (S32 j = 0; j < model->getNumVolumeFaces(); ++j) + { + const LLVolumeFace& face = model->getVolumeFace(j); + + LLVector4a center; + center.setAdd(face.mExtents[0], face.mExtents[1]); + center.mul(0.5f); + LLVector4a size; + size.setSub(face.mExtents[1],face.mExtents[0]); + size.mul(0.5f); + + for (U32 i = 0; i < 8; i++) + { + LLVector4a t; + t.setMul(size, box[i]); + t.add(center); + + LLVector4a v; + + mat.affineTransform(t, v); + + if (first_transform) + { + first_transform = FALSE; + min = max = v; + } + else + { + update_min_max(min, max, v); + } + } + } +} + +void stretch_extents(LLModel* model, LLMatrix4& mat, LLVector3& min, LLVector3& max, BOOL& first_transform) +{ + LLVector4a mina, maxa; + LLMatrix4a mata; + + mata.loadu(mat); + mina.load3(min.mV); + maxa.load3(max.mV); + + stretch_extents(model, mata, mina, maxa, first_transform); + + min.set(mina.getF32ptr()); + max.set(maxa.getF32ptr()); +} + +void LLModelLoader::run() +{ + DAE dae; + domCOLLADA* dom = dae.open(mFilename); + + if (dom) + { + daeDatabase* db = dae.getDatabase(); + + daeInt count = db->getElementCount(NULL, COLLADA_TYPE_MESH); + + daeDocument* doc = dae.getDoc(mFilename); + if (!doc) + { + llwarns << "can't find internal doc" << llendl; + return; + } + + daeElement* root = doc->getDomRoot(); + if (!root) + { + llwarns << "document has no root" << llendl; + return; + } + + //get unit scale + mTransform.setIdentity(); + + domAsset::domUnit* unit = daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUnit::ID()))); + + if (unit) + { + F32 meter = unit->getMeter(); + mTransform.mMatrix[0][0] = meter; + mTransform.mMatrix[1][1] = meter; + mTransform.mMatrix[2][2] = meter; + } + + //get up axis rotation + LLMatrix4 rotation; + + domUpAxisType up = UPAXISTYPE_Y_UP; // default is Y_UP + domAsset::domUp_axis* up_axis = + daeSafeCast(root->getDescendant(daeElement::matchType(domAsset::domUp_axis::ID()))); + + if (up_axis) + { + up = up_axis->getValue(); + } + + if (up == UPAXISTYPE_X_UP) + { + rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); + } + else if (up == UPAXISTYPE_Y_UP) + { + rotation.initRotation(90.0f * DEG_TO_RAD, 0.0f, 0.0f); + } + + rotation *= mTransform; + mTransform = rotation; + + + for (daeInt idx = 0; idx < count; ++idx) + { //build map of domEntities to LLModel + domMesh* mesh = NULL; + db->getElement((daeElement**) &mesh, idx, NULL, COLLADA_TYPE_MESH); + + if (mesh) + { + LLPointer model = LLModel::loadModelFromDomMesh(mesh); + + if (model.notNull() && validate_model(model)) + { + mModelList.push_back(model); + mModel[mesh] = model; + } + } + } + + count = db->getElementCount(NULL, COLLADA_TYPE_SKIN); + for (daeInt idx = 0; idx < count; ++idx) + { //add skinned meshes as instances + domSkin* skin = NULL; + db->getElement((daeElement**) &skin, idx, NULL, COLLADA_TYPE_SKIN); + + if (skin) + { + domGeometry* geom = daeSafeCast(skin->getSource().getElement()); + + if (geom) + { + domMesh* mesh = geom->getMesh(); + if (mesh) + { + LLModel* model = mModel[mesh]; + if (model) + { + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 normalized_transformation; + normalized_transformation.setTranslation(mesh_translation_vector); + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= normalized_transformation; + normalized_transformation = mesh_scale; + + glh::matrix4f inv_mat((F32*) normalized_transformation.mMatrix); + inv_mat = inv_mat.inverse(); + LLMatrix4 inverse_normalized_transformation(inv_mat.m); + + domSkin::domBind_shape_matrix* bind_mat = skin->getBind_shape_matrix(); + + if (bind_mat) + { //get bind shape matrix + domFloat4x4& dom_value = bind_mat->getValue(); + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + model->mBindShapeMatrix.mMatrix[i][j] = dom_value[i + j*4]; + } + } + + LLMatrix4 trans = normalized_transformation; + trans *= model->mBindShapeMatrix; + model->mBindShapeMatrix = trans; + + } + + /*{ + LLMatrix4 rotation; + if (up == UPAXISTYPE_X_UP) + { + rotation.initRotation(0.0f, 90.0f * DEG_TO_RAD, 0.0f); + } + else if (up == UPAXISTYPE_Z_UP) + { + rotation.initRotation(90.0f * DEG_TO_RAD, 90.0f * DEG_TO_RAD, 0.0f); + } + + rotation *= model->mBindShapeMatrix; + model->mBindShapeMatrix = rotation; + }*/ + + + domSkin::domJoints* joints = skin->getJoints(); + + domInputLocal_Array& joint_input = joints->getInput_array(); + + for (size_t i = 0; i < joint_input.getCount(); ++i) + { + domInputLocal* input = joint_input.get(i); + xsNMTOKEN semantic = input->getSemantic(); + + if (strcmp(semantic, COMMON_PROFILE_INPUT_JOINT) == 0) + { //found joint source, fill model->mJointMap and model->mJointList + daeElement* elem = input->getSource().getElement(); + + domSource* source = daeSafeCast(elem); + if (source) + { + + + domName_array* names_source = source->getName_array(); + + if (names_source) + { + domListOfNames &names = names_source->getValue(); + + for (size_t j = 0; j < names.getCount(); ++j) + { + std::string name(names.get(j)); + if (mJointMap.find(name) != mJointMap.end()) + { + name = mJointMap[name]; + } + model->mJointList.push_back(name); + model->mJointMap[name] = j; + } + } + else + { + domIDREF_array* names_source = source->getIDREF_array(); + if (names_source) + { + xsIDREFS& names = names_source->getValue(); + + for (size_t j = 0; j < names.getCount(); ++j) + { + std::string name(names.get(j).getID()); + if (mJointMap.find(name) != mJointMap.end()) + { + name = mJointMap[name]; + } + model->mJointList.push_back(name); + model->mJointMap[name] = j; + } + } + } + } + } + else if (strcmp(semantic, COMMON_PROFILE_INPUT_INV_BIND_MATRIX) == 0) + { //found inv_bind_matrix array, fill model->mInvBindMatrix + domSource* source = daeSafeCast(input->getSource().getElement()); + if (source) + { + domFloat_array* t = source->getFloat_array(); + if (t) + { + domListOfFloats& transform = t->getValue(); + S32 count = transform.getCount()/16; + + for (S32 k = 0; k < count; ++k) + { + LLMatrix4 mat; + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + mat.mMatrix[i][j] = transform[k*16 + i + j*4]; + } + } + + model->mInvBindMatrix.push_back(mat); + } + } + } + } + } + + + //grab raw position array + + domVertices* verts = mesh->getVertices(); + if (verts) + { + domInputLocal_Array& inputs = verts->getInput_array(); + for (size_t i = 0; i < inputs.getCount() && model->mPosition.empty(); ++i) + { + if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_POSITION) == 0) + { + domSource* pos_source = daeSafeCast(inputs[i]->getSource().getElement()); + if (pos_source) + { + domFloat_array* pos_array = pos_source->getFloat_array(); + if (pos_array) + { + domListOfFloats& pos = pos_array->getValue(); + + for (size_t j = 0; j < pos.getCount(); j += 3) + { + if (pos.getCount() <= j+2) + { + llerrs << "WTF?" << llendl; + } + + LLVector3 v(pos[j], pos[j+1], pos[j+2]); + + //transform from COLLADA space to volume space + v = v * inverse_normalized_transformation; + + model->mPosition.push_back(v); + } + } + } + } + } + } + + //grab skin weights array + domSkin::domVertex_weights* weights = skin->getVertex_weights(); + if (weights) + { + domInputLocalOffset_Array& inputs = weights->getInput_array(); + domFloat_array* vertex_weights = NULL; + for (size_t i = 0; i < inputs.getCount(); ++i) + { + if (strcmp(inputs[i]->getSemantic(), COMMON_PROFILE_INPUT_WEIGHT) == 0) + { + domSource* weight_source = daeSafeCast(inputs[i]->getSource().getElement()); + if (weight_source) + { + vertex_weights = weight_source->getFloat_array(); + } + } + } + + if (vertex_weights) + { + domListOfFloats& w = vertex_weights->getValue(); + domListOfUInts& vcount = weights->getVcount()->getValue(); + domListOfInts& v = weights->getV()->getValue(); + + U32 c_idx = 0; + for (size_t vc_idx = 0; vc_idx < vcount.getCount(); ++vc_idx) + { //for each vertex + daeUInt count = vcount[vc_idx]; + + //create list of weights that influence this vertex + LLModel::weight_list weight_list; + + for (daeUInt i = 0; i < count; ++i) + { //for each weight + daeInt joint_idx = v[c_idx++]; + daeInt weight_idx = v[c_idx++]; + + if (joint_idx == -1) + { + //ignore bindings to bind_shape_matrix + continue; + } + + F32 weight_value = w[weight_idx]; + + weight_list.push_back(LLModel::JointWeight(joint_idx, weight_value)); + } + + //sort by joint weight + std::sort(weight_list.begin(), weight_list.end(), LLModel::CompareWeightGreater()); + + std::vector wght; + + F32 total = 0.f; + + for (U32 i = 0; i < llmin((U32) 4, (U32) weight_list.size()); ++i) + { //take up to 4 most significant weights + if (weight_list[i].mWeight > 0.f) + { + wght.push_back( weight_list[i] ); + total += weight_list[i].mWeight; + } + } + + F32 scale = 1.f/total; + if (scale != 1.f) + { //normalize weights + for (U32 i = 0; i < wght.size(); ++i) + { + wght[i].mWeight *= scale; + } + } + + model->mSkinWeights[model->mPosition[vc_idx]] = wght; + } + + //add instance to scene for this model + + LLMatrix4 transform; + std::vector materials; + materials.resize(model->getNumVolumeFaces()); + mScene[transform].push_back(LLModelInstance(model, transform, materials)); + stretch_extents(model, transform, mExtents[0], mExtents[1], mFirstTransform); + } + } + } + } + } + } + } + + daeElement* scene = root->getDescendant("visual_scene"); + if (!scene) + { + llwarns << "document has no visual_scene" << llendl; + return; + } + + processElement(scene); + + mPreview->loadModelCallback(mLod); + } +} + +void LLModelLoader::processElement(daeElement* element) +{ + LLMatrix4 saved_transform = mTransform; + + domTranslate* translate = daeSafeCast(element); + if (translate) + { + domFloat3 dom_value = translate->getValue(); + + LLMatrix4 translation; + translation.setTranslation(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + + translation *= mTransform; + mTransform = translation; + } + + domRotate* rotate = daeSafeCast(element); + if (rotate) + { + domFloat4 dom_value = rotate->getValue(); + + LLMatrix4 rotation; + rotation.initRotTrans(dom_value[3] * DEG_TO_RAD, LLVector3(dom_value[0], dom_value[1], dom_value[2]), LLVector3(0, 0, 0)); + + rotation *= mTransform; + mTransform = rotation; + } + + domScale* scale = daeSafeCast(element); + if (scale) + { + domFloat3 dom_value = scale->getValue(); + + LLMatrix4 scaling; + scaling.initScale(LLVector3(dom_value[0], dom_value[1], dom_value[2])); + + scaling *= mTransform; + mTransform = scaling; + } + + domMatrix* matrix = daeSafeCast(element); + if (matrix) + { + domFloat4x4 dom_value = matrix->getValue(); + + LLMatrix4 matrix_transform; + + for (int i = 0; i < 4; i++) + { + for(int j = 0; j < 4; j++) + { + matrix_transform.mMatrix[i][j] = dom_value[i + j*4]; + } + } + + matrix_transform *= mTransform; + mTransform = matrix_transform; + } + + domInstance_geometry* instance_geo = daeSafeCast(element); + if (instance_geo) + { + domGeometry* geo = daeSafeCast(instance_geo->getUrl().getElement()); + if (geo) + { + domMesh* mesh = daeSafeCast(geo->getDescendant(daeElement::matchType(domMesh::ID()))); + if (mesh) + { + LLModel* model = mModel[mesh]; + if (model) + { + LLMatrix4 transformation = mTransform; + + std::vector materials = getMaterials(model, instance_geo); + + // adjust the transformation to compensate for mesh normalization + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + model->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + mesh_translation *= transformation; + transformation = mesh_translation; + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transformation; + transformation = mesh_scale; + + mScene[transformation].push_back(LLModelInstance(model, transformation, materials)); + + stretch_extents(model, transformation, mExtents[0], mExtents[1], mFirstTransform); + } + } + } + } + + domInstance_node* instance_node = daeSafeCast(element); + if (instance_node) + { + daeElement* instance = instance_node->getUrl().getElement(); + if (instance) + { + processElement(instance); + } + } + + //process children + daeTArray< daeSmartRef > children = element->getChildren(); + for (S32 i = 0; i < children.getCount(); i++) + { + processElement(children[i]); + } + + domNode* node = daeSafeCast(element); + if (node) + { //this element was a node, restore transform before processiing siblings + mTransform = saved_transform; + } +} + +std::vector LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo) +{ + std::vector materials; + for (int i = 0; i < model->mMaterialList.size(); i++) + { + LLImportMaterial import_material; + + domInstance_material* instance_mat = NULL; + + domBind_material::domTechnique_common* technique = + daeSafeCast(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); + + if (technique) + { + daeTArray< daeSmartRef > inst_materials = technique->getChildrenByType(); + for (int j = 0; j < inst_materials.getCount(); j++) + { + std::string symbol(inst_materials[j]->getSymbol()); + + if (symbol == model->mMaterialList[i]) // found the binding + { + instance_mat = inst_materials[j]; + } + } + } + + if (instance_mat) + { + domMaterial* material = daeSafeCast(instance_mat->getTarget().getElement()); + if (material) + { + domInstance_effect* instance_effect = + daeSafeCast(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); + if (instance_effect) + { + domEffect* effect = daeSafeCast(instance_effect->getUrl().getElement()); + if (effect) + { + domProfile_COMMON* profile = + daeSafeCast(effect->getDescendant(daeElement::matchType(domProfile_COMMON::ID()))); + if (profile) + { + import_material = profileToMaterial(profile); + } + } + } + } + } + + materials.push_back(import_material); + } + + return materials; +} + +LLImportMaterial LLModelLoader::profileToMaterial(domProfile_COMMON* material) +{ + LLImportMaterial mat; + mat.mFullbright = FALSE; + + daeElement* diffuse = material->getDescendant("diffuse"); + if (diffuse) + { + domCommon_color_or_texture_type_complexType::domTexture* texture = + daeSafeCast(diffuse->getDescendant("texture")); + if (texture) + { + domCommon_newparam_type_Array newparams = material->getNewparam_array(); + for (S32 i = 0; i < newparams.getCount(); i++) + { + domFx_surface_common* surface = newparams[i]->getSurface(); + if (surface) + { + domFx_surface_init_common* init = surface->getFx_surface_init_common(); + if (init) + { + domFx_surface_init_from_common_Array init_from = init->getInit_from_array(); + + if (init_from.getCount() > i) + { + domImage* image = daeSafeCast(init_from[i]->getValue().getElement()); + if (image) + { + // we only support init_from now - embedded data will come later + domImage::domInit_from* init = image->getInit_from(); + if (init) + { + std::string filename = cdom::uriToNativePath(init->getValue().str()); + + mat.mDiffuseMap = LLViewerTextureManager::getFetchedTextureFromUrl("file://" + filename, TRUE, LLViewerTexture::BOOST_PREVIEW); + mat.mDiffuseMap->setLoadedCallback(LLModelPreview::textureLoadedCallback, 0, TRUE, FALSE, this->mPreview, NULL, NULL); + + mat.mDiffuseMap->forceToSaveRawImage(); + mat.mDiffuseMapFilename = filename; + mat.mDiffuseMapLabel = getElementLabel(material); + } + } + } + } + } + } + } + + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast(diffuse->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + LLColor4 value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + mat.mDiffuseColor = value; + } + } + + daeElement* emission = material->getDescendant("emission"); + if (emission) + { + LLColor4 emission_color = getDaeColor(emission); + if (((emission_color[0] + emission_color[1] + emission_color[2]) / 3.0) > 0.25) + { + mat.mFullbright = TRUE; + } + } + + return mat; +} + +// try to get a decent label for this element +std::string LLModelLoader::getElementLabel(daeElement *element) +{ + // if we have a name attribute, use it + std::string name = element->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if we have an ID attribute, use it + if (element->getID()) + { + return std::string(element->getID()); + } + + // if we have a parent, use it + daeElement* parent = element->getParent(); + if (parent) + { + // if parent has a name, use it + std::string name = parent->getAttribute("name"); + if (name.length()) + { + return name; + } + + // if parent has an ID, use it + if (parent->getID()) + { + return std::string(parent->getID()); + } + } + + // try to use our type + daeString element_name = element->getElementName(); + if (element_name) + { + return std::string(element_name); + } + + // if all else fails, use "object" + return std::string("object"); +} + +LLColor4 LLModelLoader::getDaeColor(daeElement* element) +{ + LLColor4 value; + domCommon_color_or_texture_type_complexType::domColor* color = + daeSafeCast(element->getDescendant("color")); + if (color) + { + domFx_color_common domfx_color = color->getValue(); + value = LLColor4(domfx_color[0], domfx_color[1], domfx_color[2], domfx_color[3]); + } + + return value; +} + +//----------------------------------------------------------------------------- +// LLModelPreview +//----------------------------------------------------------------------------- + +LLModelPreview::LLModelPreview(S32 width, S32 height, LLFloaterModelPreview* fmp) +: LLViewerDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE), LLMutex(NULL) +{ + mNeedsUpdate = TRUE; + mCameraDistance = 0.f; + mCameraYaw = 0.f; + mCameraPitch = 0.f; + mCameraZoom = 1.f; + mTextureName = 0; + mPreviewLOD = 3; + mModelLoader = NULL; + + mLODMode[0] = 0; + + for (U32 i = 1; i < LLModel::NUM_LODS; i++) + { + mLODMode[i] = 1; + mLimit[i] = 0; + } + + mFMP = fmp; + + glodInit(); +} + +LLModelPreview::~LLModelPreview() +{ + if (mModelLoader) + { + delete mModelLoader; + mModelLoader = NULL; + } + + //*HACK : *TODO : turn this back on when we understand why this crashes + //glodShutdown(); +} + +U32 LLModelPreview::calcResourceCost() +{ + rebuildUploadData(); + + U32 cost = 0; + std::set accounted; + U32 num_points = 0; + U32 num_hulls = 0; + + for (U32 i = 0; i < mUploadData.size(); ++i) + { + LLModelInstance& instance = mUploadData[i]; + + if (accounted.find(instance.mModel) == accounted.end()) + { + accounted.insert(instance.mModel); + + LLModel::physics_shape& physics_shape = instance.mLOD[LLModel::LOD_PHYSICS] ? instance.mLOD[LLModel::LOD_PHYSICS]->mPhysicsShape : instance.mModel->mPhysicsShape; + + LLSD ret = LLModel::writeModel("", + instance.mLOD[4], + instance.mLOD[3], + instance.mLOD[2], + instance.mLOD[1], + instance.mLOD[0], + physics_shape, + TRUE); + cost += gMeshRepo.calcResourceCost(ret); + + + num_hulls += physics_shape.size(); + for (U32 i = 0; i < physics_shape.size(); ++i) + { + num_points += physics_shape[i].size(); + } + } + } + + mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",num_hulls)); + mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",num_points)); + + updateStatusMessages(); + + return cost; +} + +void LLModelPreview::rebuildUploadData() +{ + mUploadData.clear(); + mTextureSet.clear(); + + //fill uploaddata instance vectors from scene data + + for (LLModelLoader::scene::iterator iter = mBaseScene.begin(); iter != mBaseScene.end(); ++iter) + { //for each transform in scene + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { //for each instance with said transform applied + LLModelInstance& instance = *model_iter; + + LLModel* base_model = instance.mModel; + + S32 idx = 0; + for (idx = 0; idx < mBaseModel.size(); ++idx) + { //find reference instance for this model + if (mBaseModel[idx] == base_model) + { + break; + } + } + + for (U32 i = 0; i < LLModel::NUM_LODS; i++) + { //fill LOD slots based on reference model index + if (!mModel[i].empty()) + { + instance.mLOD[i] = mModel[i][idx]; + } + else + { + instance.mLOD[i] = NULL; + } + } + + mUploadData.push_back(instance); + } + } +} + + +void LLModelPreview::loadModel(std::string filename, S32 lod) +{ + LLMutexLock lock(this); + + if (mModelLoader) + { + delete mModelLoader; + mModelLoader = NULL; + } + + if (filename.empty() && mBaseModel.empty()) + { + mFMP->closeFloater(false); + return; + } + + if (lod == 3 && !mGroup.empty()) + { + for (std::map::iterator iter = mGroup.begin(); iter != mGroup.end(); ++iter) + { + glodDeleteGroup(iter->second); + stop_gloderror(); + } + + for (std::map::iterator iter = mObject.begin(); iter != mObject.end(); ++iter) + { + glodDeleteObject(iter->second); + stop_gloderror(); + } + + mGroup.clear(); + mObject.clear(); + } + + mModelLoader = new LLModelLoader(filename, lod, this); + + mModelLoader->start(); + + mFMP->childSetTextArg("status", "[STATUS]", mFMP->getString("status_reading_file")); + + if (mFMP->childGetValue("description_form").asString().empty()) + { + std::string name = gDirUtilp->getBaseFileName(filename, true); + mFMP->childSetValue("description_form", name); + } + + mFMP->openFloater(); +} + +void LLModelPreview::clearIncompatible(S32 lod) +{ + for (U32 i = 0; i <= LLModel::LOD_HIGH; i++) + { //clear out any entries that aren't compatible with this model + if (i != lod) + { + if (mModel[i].size() != mModel[lod].size()) + { + mModel[i].clear(); + mScene[i].clear(); + mVertexBuffer[i].clear(); + + if (i == LLModel::LOD_HIGH) + { + mBaseModel = mModel[lod]; + mBaseScene = mScene[lod]; + mVertexBuffer[5].clear(); + } + } + } + } +} + +void LLModelPreview::loadModelCallback(S32 lod) +{ //NOT the main thread + LLMutexLock lock(this); + if (!mModelLoader) + { + return; + } + + mModel[lod] = mModelLoader->mModelList; + mScene[lod] = mModelLoader->mScene; + mVertexBuffer[lod].clear(); + + setPreviewLOD(lod); + + + if (lod == LLModel::LOD_HIGH) + { //save a copy of the highest LOD for automatic LOD manipulation + mBaseModel = mModel[lod]; + mBaseScene = mScene[lod]; + mVertexBuffer[5].clear(); + //mModel[lod] = NULL; + } + + clearIncompatible(lod); + + mResourceCost = calcResourceCost(); + + mPreviewTarget = (mModelLoader->mExtents[0] + mModelLoader->mExtents[1]) * 0.5f; + mPreviewScale = (mModelLoader->mExtents[1] - mModelLoader->mExtents[0]) * 0.5f; + setPreviewTarget(mPreviewScale.magVec()*2.f); + + mFMP->mLoading = FALSE; + refresh(); +} + +void LLModelPreview::smoothNormals() +{ + S32 which_lod = mPreviewLOD; + + + if (which_lod > 4 || which_lod < 0 || + mModel[which_lod].empty()) + { + return; + } + + F32 angle_cutoff = mFMP->childGetValue("edge threshold").asReal(); + + angle_cutoff *= DEG_TO_RAD; + + if (which_lod == 3 && !mBaseModel.empty()) + { + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { + (*iter)->smoothNormals(angle_cutoff); + } + + mVertexBuffer[5].clear(); + } + + for (LLModelLoader::model_list::iterator iter = mModel[which_lod].begin(); iter != mModel[which_lod].end(); ++iter) + { + (*iter)->smoothNormals(angle_cutoff); + } + + mVertexBuffer[which_lod].clear(); + refresh(); + +} + +void LLModelPreview::consolidate() +{ + std::map > composite; + + LLMatrix4 identity; + + //bake out each node in current scene to composite + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { //for each transform in current scene + LLMatrix4 mat = iter->first; + glh::matrix4f inv_trans = glh::matrix4f((F32*) mat.mMatrix).inverse().transpose(); + LLMatrix4 norm_mat(inv_trans.m); + + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { //for each instance with that transform + LLModelInstance& source_instance = *model_iter; + LLModel* source = source_instance.mModel; + + if (!validate_model(source)) + { + llerrs << "Invalid model found!" << llendl; + } + + for (S32 i = 0; i < source->getNumVolumeFaces(); ++i) + { //for each face in instance + const LLVolumeFace& src_face = source->getVolumeFace(i); + LLImportMaterial& source_material = source_instance.mMaterial[i]; + + //get model in composite that is composite for this material + LLModel* model = NULL; + + if (composite.find(source_material) != composite.end()) + { + model = composite[source_material].rbegin()->mModel; + if (model->getVolumeFace(0).mNumVertices + src_face.mNumVertices > 65535) + { + model = NULL; + } + } + + if (model == NULL) + { //no model found, make new model + std::vector materials; + materials.push_back(source_material); + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + model = new LLModel(volume_params, 0.f); + model->mLabel = source->mLabel; + model->setNumVolumeFaces(0); + composite[source_material].push_back(LLModelInstance(model, identity, materials)); + } + + model->appendFace(src_face, source->mMaterialList[i], mat, norm_mat); + } + } + } + + + //condense composite into as few LLModel instances as possible + LLModelLoader::model_list new_model; + std::vector instance_list; + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + + std::vector empty_material; + LLModelInstance cur_instance(new LLModel(volume_params, 0.f), identity, empty_material); + cur_instance.mModel->setNumVolumeFaces(0); + + BOOL first_transform = TRUE; + + LLModelLoader::scene new_scene; + LLVector3 min,max; + + for (std::map >::iterator iter = composite.begin(); + iter != composite.end(); + ++iter) + { + std::map >::iterator next_iter = iter; ++next_iter; + + for (std::vector::iterator instance_iter = iter->second.begin(); + instance_iter != iter->second.end(); + ++instance_iter) + { + LLModel* source = instance_iter->mModel; + + if (instance_iter->mMaterial.size() != 1) + { + llerrs << "WTF?" << llendl; + } + + if (source->getNumVolumeFaces() != 1) + { + llerrs << "WTF?" << llendl; + } + + if (source->mMaterialList.size() != 1) + { + llerrs << "WTF?" << llendl; + } + + cur_instance.mModel->addFace(source->getVolumeFace(0)); + cur_instance.mMaterial.push_back(instance_iter->mMaterial[0]); + cur_instance.mModel->mMaterialList.push_back(source->mMaterialList[0]); + + BOOL last_model = FALSE; + + std::vector::iterator next_instance = instance_iter; ++next_instance; + + if (next_iter == composite.end() && + next_instance == iter->second.end()) + { + last_model = TRUE; + } + + if (last_model || cur_instance.mModel->getNumVolumeFaces() >= MAX_MODEL_FACES) + { + cur_instance.mModel->mLabel = source->mLabel; + + cur_instance.mModel->optimizeVolumeFaces(); + cur_instance.mModel->normalizeVolumeFaces(); + + if (!validate_model(cur_instance.mModel)) + { + llerrs << "Invalid model detected." << llendl; + } + + new_model.push_back(cur_instance.mModel); + + LLMatrix4 transformation = LLMatrix4(); + + // adjust the transformation to compensate for mesh normalization + LLVector3 mesh_scale_vector; + LLVector3 mesh_translation_vector; + cur_instance.mModel->getNormalizedScaleTranslation(mesh_scale_vector, mesh_translation_vector); + + LLMatrix4 mesh_translation; + mesh_translation.setTranslation(mesh_translation_vector); + mesh_translation *= transformation; + transformation = mesh_translation; + + LLMatrix4 mesh_scale; + mesh_scale.initScale(mesh_scale_vector); + mesh_scale *= transformation; + transformation = mesh_scale; + + cur_instance.mTransform = transformation; + + new_scene[transformation].push_back(cur_instance); + stretch_extents(cur_instance.mModel, transformation, min, max, first_transform); + + if (!last_model) + { + cur_instance = LLModelInstance(new LLModel(volume_params, 0.f), identity, empty_material); + cur_instance.mModel->setNumVolumeFaces(0); + } + } + } + } + + mScene[mPreviewLOD] = new_scene; + mModel[mPreviewLOD] = new_model; + mVertexBuffer[mPreviewLOD].clear(); + + if (mPreviewLOD == LLModel::LOD_HIGH) + { + mBaseScene = new_scene; + mBaseModel = new_model; + mVertexBuffer[5].clear(); + } + + mPreviewTarget = (min+max)*0.5f; + mPreviewScale = (max-min)*0.5f; + setPreviewTarget(mPreviewScale.magVec()*2.f); + + clearIncompatible(mPreviewLOD); + + mResourceCost = calcResourceCost(); + refresh(); +} + +void LLModelPreview::scrubMaterials() +{ + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { //for each transform in current scene + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { //for each instance with that transform + LLModelInstance& source_instance = *model_iter; + LLModel* source = source_instance.mModel; + + for (S32 i = 0; i < source->getNumVolumeFaces(); ++i) + { //for each face in instance + LLImportMaterial& source_material = source_instance.mMaterial[i]; + + //clear material info + source_material.mDiffuseColor = LLColor4(1,1,1,1); + source_material.mDiffuseMap = NULL; + source_material.mDiffuseMapFilename.clear(); + source_material.mDiffuseMapLabel.clear(); + source_material.mFullbright = false; + } + } + } + + + mVertexBuffer[mPreviewLOD].clear(); + + if (mPreviewLOD == LLModel::LOD_HIGH) + { + mBaseScene = mScene[mPreviewLOD]; + mBaseModel = mModel[mPreviewLOD]; + mVertexBuffer[5].clear(); + } + + mResourceCost = calcResourceCost(); + refresh(); +} + +void LLModelPreview::genLODs(S32 which_lod) +{ + if (mBaseModel.empty()) + { + return; + } + + LLVertexBuffer::unbind(); + + stop_gloderror(); + static U32 cur_name = 1; + + S32 limit = -1; + + if (which_lod != -1) + { + limit = mLimit[which_lod]; + } + + U32 triangle_count = 0; + + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { + LLModel* mdl = *iter; + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + triangle_count += mdl->getVolumeFace(i).mNumIndices/3; + } + } + + U32 base_triangle_count = triangle_count; + + U32 type_mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + for (LLModelLoader::model_list::iterator iter = mBaseModel.begin(); iter != mBaseModel.end(); ++iter) + { //build GLOD objects for each model in base model list + LLModel* mdl = *iter; + if (mGroup[mdl] == 0) + { + mGroup[mdl] = cur_name++; + mObject[mdl] = cur_name++; + + glodNewGroup(mGroup[mdl]); + stop_gloderror(); + + glodGroupParameteri(mGroup[mdl], GLOD_ADAPT_MODE, GLOD_TRIANGLE_BUDGET); + stop_gloderror(); + + glodGroupParameteri(mGroup[mdl], GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + + glodGroupParameterf(mGroup[mdl], GLOD_OBJECT_SPACE_ERROR_THRESHOLD, 0.025f); + stop_gloderror(); + + glodNewObject(mObject[mdl], mGroup[mdl], GLOD_DISCRETE); + stop_gloderror(); + + if (iter == mBaseModel.begin() && !mdl->mSkinWeights.empty()) + { //regenerate vertex buffer for skinned models to prevent animation feedback during LOD generation + mVertexBuffer[5].clear(); + } + + if (mVertexBuffer[5].empty()) + { + genBuffers(5); + } + + U32 tri_count = 0; + for (U32 i = 0; i < mVertexBuffer[5][mdl].size(); ++i) + { + mVertexBuffer[5][mdl][i]->setBuffer(type_mask); + U32 num_indices = mVertexBuffer[5][mdl][i]->getNumIndices(); + if (num_indices > 2) + { + glodInsertElements(mObject[mdl], i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, mVertexBuffer[5][mdl][i]->getIndicesPointer(), 0, 0.f); + } + tri_count += num_indices/3; + stop_gloderror(); + } + + //store what percentage of total model (in terms of triangle count) this model makes up + mPercentage[mdl] = (F32) tri_count / (F32) base_triangle_count; + + //build glodobject + glodBuildObject(mObject[mdl]); + if (stop_gloderror()) + { + glodDeleteGroup(mGroup[mdl]); + stop_gloderror(); + glodDeleteObject(mObject[mdl]); + stop_gloderror(); + + mGroup[mdl] = 0; + mObject[mdl] = 0; + + if (which_lod == -1) + { + mModel[LLModel::LOD_HIGH] = mBaseModel; + } + + return; + } + + } + + if (which_lod == -1 || mLODMode[which_lod] == 1) + { + //generating LODs for all entries, or this entry has a triangle budget + glodGroupParameteri(mGroup[mdl], GLOD_ADAPT_MODE, GLOD_TRIANGLE_BUDGET); + stop_gloderror(); + } + else + { + //this entry uses error mode + glodGroupParameteri(mGroup[mdl], GLOD_ADAPT_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + } + + if (which_lod != -1 && mLODMode[which_lod] == 2) + { + glodGroupParameterf(mGroup[mdl], GLOD_OBJECT_SPACE_ERROR_THRESHOLD, llmax(limit/100.f, 0.01f)); + stop_gloderror(); + } + else + { + glodGroupParameterf(mGroup[mdl], GLOD_OBJECT_SPACE_ERROR_THRESHOLD, 0.025f); + stop_gloderror(); + } + } + + + S32 start = LLModel::LOD_HIGH; + S32 end = 0; + + BOOL error_mode = FALSE; + + if (which_lod != -1) + { + start = end = which_lod; + + if (mLODMode[which_lod] == 2) + { + error_mode = TRUE; + } + } + + + for (S32 lod = start; lod >= end; --lod) + { + if (!error_mode) + { + if (which_lod == -1) + { + if (lod < start) + { + triangle_count /= 3; + } + } + else + { + triangle_count = limit; + } + } + + mModel[lod].clear(); + mModel[lod].resize(mBaseModel.size()); + mVertexBuffer[lod].clear(); + + U32 actual_tris = 0; + U32 actual_verts = 0; + U32 submeshes = 0; + + for (U32 mdl_idx = 0; mdl_idx < mBaseModel.size(); ++mdl_idx) + { + LLModel* base = mBaseModel[mdl_idx]; + + U32 target_count = U32(mPercentage[base]*triangle_count); + + if (error_mode) + { + target_count = base->getNumTriangles(); + } + + if (target_count < 4) + { + target_count = 4; + } + + if (which_lod == -1 || mLODMode[which_lod] == 1) + { + glodGroupParameteri(mGroup[base], GLOD_MAX_TRIANGLES, target_count); + stop_gloderror(); + } + + glodAdaptGroup(mGroup[base]); + stop_gloderror(); + + GLint patch_count = 0; + glodGetObjectParameteriv(mObject[base], GLOD_NUM_PATCHES, &patch_count); + stop_gloderror(); + + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + mModel[lod][mdl_idx] = new LLModel(volume_params, 0.f); + + GLint* sizes = new GLint[patch_count*2]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_SIZES, sizes); + stop_gloderror(); + + GLint* names = new GLint[patch_count]; + glodGetObjectParameteriv(mObject[base], GLOD_PATCH_NAMES, names); + stop_gloderror(); + + mModel[lod][mdl_idx]->setNumVolumeFaces(patch_count); + + LLModel* target_model = mModel[lod][mdl_idx]; + + for (GLint i = 0; i < patch_count; ++i) + { + LLPointer buff = new LLVertexBuffer(type_mask, 0); + + if (sizes[i*2+1] > 0 && sizes[i*2] > 0) + { + buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true); + buff->setBuffer(type_mask); + glodFillElements(mObject[base], names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer()); + stop_gloderror(); + } + else + { //this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0) + buff->allocateBuffer(1, 3, true); + memset(buff->getMappedData(), 0, buff->getSize()); + memset(buff->getIndicesPointer(), 0, buff->getIndicesSize()); + } + + buff->validateRange(0, buff->getNumVerts()-1, buff->getNumIndices(), 0); + + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; + + buff->getVertexStrider(pos); + buff->getNormalStrider(norm); + buff->getTexCoord0Strider(tc); + buff->getIndexStrider(index); + + + target_model->setVolumeFaceData(names[i], pos, norm, tc, index, buff->getNumVerts(), buff->getNumIndices()); + actual_tris += buff->getNumIndices()/3; + actual_verts += buff->getNumVerts(); + ++submeshes; + + if (!validate_face(target_model->getVolumeFace(names[i]))) + { + llerrs << "Invalid face generated during LOD generation." << llendl; + } + } + + //blind copy skin weights and just take closest skin weight to point on + //decimated mesh for now (auto-generating LODs with skin weights is still a bit + //of an open problem). + target_model->mPosition = base->mPosition; + target_model->mSkinWeights = base->mSkinWeights; + target_model->mJointMap = base->mJointMap; + target_model->mJointList = base->mJointList; + target_model->mInvBindMatrix = base->mInvBindMatrix; + target_model->mBindShapeMatrix = base->mBindShapeMatrix; + + if (!validate_model(target_model)) + { + llerrs << "Invalid model generated when creating LODs" << llendl; + } + + delete [] sizes; + delete [] names; + } + + //rebuild scene based on mBaseScene + mScene[lod].clear(); + mScene[lod] = mBaseScene; + + for (U32 i = 0; i < mBaseModel.size(); ++i) + { + LLModel* mdl = mBaseModel[i]; + LLModel* target = mModel[lod][i]; + if (target) + { + for (LLModelLoader::scene::iterator iter = mScene[lod].begin(); iter != mScene[lod].end(); ++iter) + { + for (U32 j = 0; j < iter->second.size(); ++j) + { + if (iter->second[j].mModel == mdl) + { + iter->second[j].mModel = target; + } + } + } + } + } + + mResourceCost = calcResourceCost(); + } +} + +void LLModelPreview::updateStatusMessages() +{ + //triangle/vertex/submesh count for each mesh asset for each lod + std::vector tris[LLModel::NUM_LODS]; + std::vector verts[LLModel::NUM_LODS]; + std::vector submeshes[LLModel::NUM_LODS]; + + //total triangle/vertex/submesh count for each lod + S32 total_tris[LLModel::NUM_LODS]; + S32 total_verts[LLModel::NUM_LODS]; + S32 total_submeshes[LLModel::NUM_LODS]; + + for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) + { + //initialize total for this lod to 0 + total_tris[lod] = total_verts[lod] = total_submeshes[lod] = 0; + + for (U32 i = 0; i < mModel[lod].size(); ++i) + { //for each model in the lod + S32 cur_tris = 0; + S32 cur_verts = 0; + S32 cur_submeshes = mModel[lod][i]->getNumVolumeFaces(); + + for (S32 j = 0; j < cur_submeshes; ++j) + { //for each submesh (face), add triangles and vertices to current total + const LLVolumeFace& face = mModel[lod][i]->getVolumeFace(j); + cur_tris += face.mNumIndices/3; + cur_verts += face.mNumVertices; + } + + //add this model to the lod total + total_tris[lod] += cur_tris; + total_verts[lod] += cur_verts; + total_submeshes[lod] += cur_submeshes; + + //store this model's counts to asset data + tris[lod].push_back(cur_tris); + verts[lod].push_back(cur_verts); + submeshes[lod].push_back(cur_submeshes); + } + } + + + std::string upload_message; + + for (S32 lod = 0; lod <= LLModel::LOD_HIGH; ++lod) + { + mFMP->childSetTextArg(info_name[lod], "[TRIANGLES]", llformat("%d", total_tris[lod])); + mFMP->childSetTextArg(info_name[lod], "[VERTICES]", llformat("%d", total_verts[lod])); + mFMP->childSetTextArg(info_name[lod], "[SUBMESHES]", llformat("%d", total_submeshes[lod])); + + std::string message = "good"; + + const U32 lod_high = LLModel::LOD_HIGH; + + if (lod != lod_high) + { + if (total_submeshes[lod] == 0) + { //no model loaded for this lod, see if one is required + for (U32 i = 0; i < verts[lod_high].size(); ++i) + { + const F32 ratio = 0.5f; + const S32 required_verts = 128; + + F32 scaler = powf(0.5f, lod_high-lod); + S32 max_verts = verts[lod_high][i]*scaler; + + if (max_verts > required_verts) + { //some model in this slot might have more than 128 vertices + + //if any model higher up the chain has more than 128 vertices, + // lod is required here + for (S32 j = lod+1; j <= LLModel::LOD_HIGH; ++j) + { + if (verts[j].size() > i && verts[j][i] > 128) + { + message = "required"; + upload_message = "missing_lod"; + } + } + } + } + } + else if (total_submeshes[lod] != total_submeshes[lod_high]) + { + message = "mesh_mismatch"; + upload_message = "bad_lod"; + } + else if (tris[lod].size() != tris[lod_high].size()) + { + message = "model_mismatch"; + upload_message = "bad_lod"; + } + else + { + for (U32 i = 0; i < verts[lod].size(); ++i) + { + const F32 ratio = 0.5f; + + F32 scaler = powf(0.5f, lod_high-lod); + S32 max_verts = verts[lod_high][i]*scaler; + + if (verts[lod][i] > max_verts) + { + message = "too_heavy"; + upload_message = "bad_lod"; + } + } + } + } + + mFMP->childSetTextArg(info_name[lod], "[MESSAGE]", mFMP->getString(message)); + } + + if (upload_message.empty()) + { + mFMP->childSetTextArg("upload_message", "[MESSAGE]", std::string("")); + mFMP->childEnable("ok_btn"); + } + else + { + mFMP->childSetTextArg("upload_message", "[MESSAGE]", mFMP->getString(upload_message)); + mFMP->childDisable("ok_btn"); + } +} + +void LLModelPreview::setPreviewTarget(F32 distance) +{ + mCameraDistance = distance; + mCameraZoom = 1.f; + mCameraPitch = 0.f; + mCameraYaw = 0.f; + mCameraOffset.clearVec(); +} + +void LLModelPreview::genBuffers(S32 lod) +{ + U32 tri_count = 0; + U32 vertex_count = 0; + U32 mesh_count = 0; + + LLModelLoader::model_list* model = NULL; + + if (lod < 0 || lod > 4) + { + model = &mBaseModel; + lod = 5; + } + else + { + model = &(mModel[lod]); + } + + if (!mVertexBuffer[lod].empty()) + { + mVertexBuffer[lod].clear(); + } + + mVertexBuffer[lod].clear(); + + LLModelLoader::model_list::iterator base_iter = mBaseModel.begin(); + + for (LLModelLoader::model_list::iterator iter = model->begin(); iter != model->end(); ++iter) + { + LLModel* mdl = *iter; + if (!mdl) + { + continue; + } + + LLModel* base_mdl = *base_iter; + base_iter++; + + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + const LLVolumeFace &vf = mdl->getVolumeFace(i); + U32 num_vertices = vf.mNumVertices; + U32 num_indices = vf.mNumIndices; + + if (!num_vertices || ! num_indices) + { + continue; + } + + LLVertexBuffer* vb = NULL; + + bool skinned = !mdl->mSkinWeights.empty(); + + U32 mask = LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0; + + if (skinned) + { + mask |= LLVertexBuffer::MAP_WEIGHT4; + } + + vb = new LLVertexBuffer(mask, 0); + + vb->allocateBuffer(num_vertices, num_indices, TRUE); + + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + LLStrider index_strider; + LLStrider weights_strider; + + vb->getVertexStrider(vertex_strider); + vb->getNormalStrider(normal_strider); + vb->getTexCoord0Strider(tc_strider); + vb->getIndexStrider(index_strider); + + if (skinned) + { + vb->getWeight4Strider(weights_strider); + } + + LLVector4a::memcpyNonAliased16((F32*) vertex_strider.get(), (F32*) vf.mPositions, num_vertices*4*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) tc_strider.get(), (F32*) vf.mTexCoords, num_vertices*2*sizeof(F32)); + LLVector4a::memcpyNonAliased16((F32*) normal_strider.get(), (F32*) vf.mNormals, num_vertices*4*sizeof(F32)); + + if (skinned) + { + // build vertices and normals + for (U32 i = 0; i < num_vertices; i++) + { + //find closest weight to vf.mVertices[i].mPosition + LLVector3 pos(vf.mPositions[i].getF32ptr()); + + LLModel::weight_list weight_list = base_mdl->getJointInfluences(pos); + + LLVector4 w(0,0,0,0); + if (weight_list.size() > 4) + { + llerrs << "WTF?" << llendl; + } + + for (U32 i = 0; i < weight_list.size(); ++i) + { + F32 wght = llmin(weight_list[i].mWeight, 0.999999f); + F32 joint = (F32) weight_list[i].mJointIdx; + w.mV[i] = joint + wght; + } + + *(weights_strider++) = w; + } + } + + // build indices + for (U32 i = 0; i < num_indices; i++) + { + *(index_strider++) = vf.mIndices[i]; + } + + mVertexBuffer[lod][mdl].push_back(vb); + + vertex_count += num_vertices; + tri_count += num_indices/3; + ++mesh_count; + + } + } + + if (lod == 4) + { + for (U32 i = 0; i < 4; i++) + { + LLSpinCtrl* lim = mFMP->getChild(limit_name[i], TRUE); + + lim->setMaxValue(tri_count); + } + } +} + +//----------------------------------------------------------------------------- +// render() +//----------------------------------------------------------------------------- +BOOL LLModelPreview::render() +{ + LLMutexLock lock(this); + mNeedsUpdate = FALSE; + + S32 width = getWidth(); + S32 height = getHeight(); + + LLGLSUIDefault def; + LLGLDisable no_blend(GL_BLEND); + LLGLEnable cull(GL_CULL_FACE); + LLGLDepthTest depth(GL_TRUE); + LLGLDisable fog(GL_FOG); + + glMatrixMode(GL_PROJECTION); + gGL.pushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, width, 0.0f, height, -1.0f, 1.0f); + + glMatrixMode(GL_MODELVIEW); + gGL.pushMatrix(); + glLoadIdentity(); + + gGL.color4f(0.15f, 0.2f, 0.3f, 1.f); + + gl_rect_2d_simple( width, height ); + + bool avatar_preview = false; + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + if (!model->mSkinWeights.empty()) + { + avatar_preview = true; + } + } + } + + mFMP->childSetEnabled("consolidate", !avatar_preview); + + F32 explode = mFMP->childGetValue("explode").asReal(); + + glMatrixMode(GL_PROJECTION); + gGL.popMatrix(); + + glMatrixMode(GL_MODELVIEW); + gGL.popMatrix(); + + glClear(GL_DEPTH_BUFFER_BIT); + + LLViewerCamera::getInstance()->setAspect((F32) width / height ); + LLViewerCamera::getInstance()->setView(LLViewerCamera::getInstance()->getDefaultFOV() / mCameraZoom); + + LLVector3 target_pos = mPreviewTarget; + LLVector3 offset = mCameraOffset; + + F32 z_near = llmax(mCameraDistance-mPreviewScale.magVec(), 0.001f); + F32 z_far = mCameraDistance+mPreviewScale.magVec(); + + if (avatar_preview) + { + target_pos = gAgentAvatarp->getPositionAgent(); + z_near = 0.01f; + z_far = 1024.f; + mCameraDistance = 16.f; + + //render avatar previews every frame + refresh(); + } + + LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) * + LLQuaternion(mCameraYaw, LLVector3::z_axis); + + LLQuaternion av_rot = camera_rot; + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + + LLViewerCamera::getInstance()->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, width, height, FALSE, z_near, z_far); + + stop_glerror(); + + gPipeline.enableLightsAvatar(); + + gGL.pushMatrix(); + const F32 BRIGHTNESS = 0.9f; + gGL.color3f(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS); + + LLGLEnable normalize(GL_NORMALIZE); + + if (!mBaseModel.empty() && mVertexBuffer[5].empty()) + { + genBuffers(-1); + genBuffers(3); + //genLODs(); + } + + bool physics = (mPreviewLOD == LLModel::LOD_PHYSICS); + + S32 physics_idx = -1; + + bool render_mesh = true; + bool render_hull = false; + + if (physics && mFMP->mDecompFloater) + { + physics_idx = mFMP->mDecompFloater->childGetValue("model").asInteger(); + render_mesh = mFMP->mDecompFloater->childGetValue("render_mesh").asBoolean(); + render_hull = mFMP->mDecompFloater->childGetValue("render_hull").asBoolean(); + } + + if (!mModel[mPreviewLOD].empty()) + { + if (mVertexBuffer[mPreviewLOD].empty()) + { + genBuffers(mPreviewLOD); + } + + if (!avatar_preview) + { + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + gGL.pushMatrix(); + LLMatrix4 mat = iter->first; + + glMultMatrixf((GLfloat*) mat.mMatrix); + + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + + if (instance.mTransform != mat) + { + llerrs << "WTF?" << llendl; + } + + if (render_mesh) + { + for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + if (physics) + { + if (physics_idx > -1 && model == mModel[mPreviewLOD][physics_idx]) + { + glColor4f(1,0,0,1); + } + else + { + glColor4f(0.75f, 0.75f, 0.75f, 1.f); + } + } + else + { + glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); + if (i < instance.mMaterial.size() && instance.mMaterial[i].mDiffuseMap.notNull()) + { + gGL.getTexUnit(0)->bind(instance.mMaterial[i].mDiffuseMap, true); + if (instance.mMaterial[i].mDiffuseMap->getDiscardLevel() > -1) + { + mTextureSet.insert(instance.mMaterial[i].mDiffuseMap); + } + } + } + + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + glColor3f(0.4f, 0.4f, 0.4f); + + if (mFMP->childGetValue("show edges").asBoolean()) + { + glLineWidth(3.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->drawRange(LLRender::TRIANGLES, 0, buffer->getNumVerts()-1, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + } + + if (render_hull) + { + LLPhysicsDecomp* decomp = gMeshRepo.mDecompThread; + if (decomp) + { + LLMutexLock(decomp->mMutex); + + std::map > >::iterator iter = + mPhysicsMesh.find(model); + if (iter != mPhysicsMesh.end()) + { + for (U32 i = 0; i < iter->second.size(); ++i) + { + if (explode > 0.f) + { + gGL.pushMatrix(); + + LLVector3 offset = model->mHullCenter[i]-model->mPhysicsCenter; + offset *= explode; + + gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]); + } + + static std::vector hull_colors; + + if (i+1 >= hull_colors.size()) + { + hull_colors.push_back(LLColor4U(rand()%128+127, rand()%128+127, rand()%128+127, 255)); + } + + LLVertexBuffer* buff = iter->second[i]; + if (buff) + { + buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL); + + glColor4ubv(hull_colors[i].mV); + buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); + + if (mFMP->childGetValue("show edges").asBoolean()) + { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glLineWidth(3.f); + glColor4ub(hull_colors[i].mV[0]/2, hull_colors[i].mV[1]/2, hull_colors[i].mV[2]/2, 255); + buff->drawArrays(LLRender::TRIANGLES, 0, buff->getNumVerts()); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + + if (explode > 0.f) + { + gGL.popMatrix(); + } + } + } + } + + //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[HULLS]", llformat("%d",decomp->mHulls.size())); + //mFMP->childSetTextArg(info_name[LLModel::LOD_PHYSICS], "[POINTS]", llformat("%d",decomp->mTotalPoints)); + } + } + + gGL.popMatrix(); + } + + if (physics) + { + mPreviewLOD = LLModel::LOD_PHYSICS; + } + } + else + { + LLVOAvatarSelf* avatar = gAgentAvatarp; + target_pos = avatar->getPositionAgent(); + + LLViewerCamera::getInstance()->setOriginAndLookAt( + target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + offset) * av_rot), // camera + LLVector3::z_axis, // up + target_pos); // point of interest + + avatar->renderCollisionVolumes(); + + for (LLModelLoader::scene::iterator iter = mScene[mPreviewLOD].begin(); iter != mScene[mPreviewLOD].end(); ++iter) + { + for (LLModelLoader::model_instance_list::iterator model_iter = iter->second.begin(); model_iter != iter->second.end(); ++model_iter) + { + LLModelInstance& instance = *model_iter; + LLModel* model = instance.mModel; + + if (!model->mSkinWeights.empty()) + { + for (U32 i = 0; i < mVertexBuffer[mPreviewLOD][model].size(); ++i) + { + LLVertexBuffer* buffer = mVertexBuffer[mPreviewLOD][model][i]; + + const LLVolumeFace& face = model->getVolumeFace(i); + + LLStrider position; + buffer->getVertexStrider(position); + + LLStrider weight; + buffer->getWeight4Strider(weight); + + //quick 'n dirty software vertex skinning + + //build matrix palette + LLMatrix4 mat[64]; + for (U32 j = 0; j < model->mJointList.size(); ++j) + { + LLJoint* joint = avatar->getJoint(model->mJointList[j]); + if (joint) + { + mat[j] = model->mInvBindMatrix[j]; + mat[j] *= joint->getWorldMatrix(); + } + } + + for (U32 j = 0; j < buffer->getRequestedVerts(); ++j) + { + LLMatrix4 final_mat; + final_mat.mMatrix[0][0] = final_mat.mMatrix[1][1] = final_mat.mMatrix[2][2] = final_mat.mMatrix[3][3] = 0.f; + + LLVector4 wght; + S32 idx[4]; + + F32 scale = 0.f; + for (U32 k = 0; k < 4; k++) + { + F32 w = weight[j].mV[k]; + + idx[k] = (S32) floorf(w); + wght.mV[k] = w - floorf(w); + scale += wght.mV[k]; + } + + wght *= 1.f/scale; + + for (U32 k = 0; k < 4; k++) + { + F32* src = (F32*) mat[idx[k]].mMatrix; + F32* dst = (F32*) final_mat.mMatrix; + + F32 w = wght.mV[k]; + + for (U32 l = 0; l < 16; l++) + { + dst[l] += src[l]*w; + } + } + + //VECTORIZE THIS + LLVector3 v(face.mPositions[j].getF32ptr()); + + v = v * model->mBindShapeMatrix; + v = v * final_mat; + + position[j] = v; + } + + buffer->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + glColor4fv(instance.mMaterial[i].mDiffuseColor.mV); + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + glColor3f(0.4f, 0.4f, 0.4f); + + if (mFMP->childGetValue("show edges").asBoolean()) + { + glLineWidth(3.f); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + buffer->draw(LLRender::TRIANGLES, buffer->getNumIndices(), 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glLineWidth(1.f); + } + } + } + } + } + } + } + + gGL.popMatrix(); + + return TRUE; +} + +//----------------------------------------------------------------------------- +// refresh() +//----------------------------------------------------------------------------- +void LLModelPreview::refresh() +{ + mNeedsUpdate = TRUE; +} + +//----------------------------------------------------------------------------- +// rotate() +//----------------------------------------------------------------------------- +void LLModelPreview::rotate(F32 yaw_radians, F32 pitch_radians) +{ + mCameraYaw = mCameraYaw + yaw_radians; + + mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f); +} + +//----------------------------------------------------------------------------- +// zoom() +//----------------------------------------------------------------------------- +void LLModelPreview::zoom(F32 zoom_amt) +{ + F32 new_zoom = mCameraZoom+zoom_amt; + + mCameraZoom = llclamp(new_zoom, 1.f, 10.f); +} + +void LLModelPreview::pan(F32 right, F32 up) +{ + mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f); + mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f); +} + +void LLModelPreview::setPreviewLOD(S32 lod) +{ + mPreviewLOD = llclamp(lod, 0, 4); + refresh(); +} + +//static +void LLFloaterModelPreview::onBrowseHighLOD(void* data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; + mp->loadModel(3); +} + +//static +void LLFloaterModelPreview::onBrowseMediumLOD(void* data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; + mp->loadModel(2); +} + +//static +void LLFloaterModelPreview::onBrowseLowLOD(void* data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; + mp->loadModel(1); +} + +//static +void LLFloaterModelPreview::onBrowseLowestLOD(void* data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) data; + mp->loadModel(0); +} + +//static +void LLFloaterModelPreview::onUpload(void* user_data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + + if (mp->mDecompFloater) + { + mp->mDecompFloater->closeFloater(); + } + + mp->mModelPreview->rebuildUploadData(); + + gMeshRepo.uploadModel(mp->mModelPreview->mUploadData, mp->mModelPreview->mPreviewScale, mp->childGetValue("upload_textures").asBoolean()); + + mp->closeFloater(false); +} + +//static +void LLFloaterModelPreview::onConsolidate(void* user_data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + mp->mModelPreview->consolidate(); +} + +//static +void LLFloaterModelPreview::onScrubMaterials(void* user_data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + mp->mModelPreview->scrubMaterials(); +} + +//static +void LLFloaterModelPreview::onDecompose(void* user_data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + mp->showDecompFloater(); +} + +//static +void LLFloaterModelPreview::onModelDecompositionComplete(LLModel* model, std::vector >& physics_mesh) +{ + if (sInstance && sInstance->mModelPreview) + { + sInstance->mModelPreview->mPhysicsMesh[model] = physics_mesh; + + sInstance->mModelPreview->mResourceCost = sInstance->mModelPreview->calcResourceCost(); + } +} + + +//static +void LLFloaterModelPreview::refresh(LLUICtrl* ctrl, void* user_data) +{ + LLFloaterModelPreview* mp = (LLFloaterModelPreview*) user_data; + mp->mModelPreview->refresh(); +} + +void LLFloaterModelPreview::updateResourceCost() +{ + U32 cost = mModelPreview->mResourceCost; + childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",cost)); +} + +//static +void LLModelPreview::textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ) +{ + LLModelPreview* preview = (LLModelPreview*) userdata; + preview->refresh(); +} + +#endif + diff --git a/indra/newview/llfloatermodelpreview.h b/indra/newview/llfloatermodelpreview.h new file mode 100644 index 0000000000..1f9de2e2b9 --- /dev/null +++ b/indra/newview/llfloatermodelpreview.h @@ -0,0 +1,275 @@ +/** + * @file llfloatermodelpreview.h + * @brief LLFloaterModelPreview class definition + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLFLOATERMODELPREVIEW_H +#define LL_LLFLOATERMODELPREVIEW_H + +#include "llfloaternamedesc.h" + +#include "lldynamictexture.h" +#include "llquaternion.h" +#include "llmeshrepository.h" +#include "llmodel.h" +#include "llthread.h" + +#if LL_MESH_ENABLED +class LLComboBox; +class LLJoint; +class LLViewerJointMesh; +class LLVOAvatar; +class LLTextBox; +class LLVertexBuffer; +class LLModelPreview; +class LLFloaterModelPreview; +class daeElement; +class domProfile_COMMON; +class domInstance_geometry; + +class LLPhysicsDecompFloater : public LLFloater +{ +public: + + LLPhysicsDecompFloater(LLSD& key); + ~LLPhysicsDecompFloater(); +}; + +class LLModelLoader : public LLThread +{ +public: + typedef enum + { + STARTING = 0, + READING_FILE, + CREATING_FACES, + GENERATING_VERTEX_BUFFERS, + GENERATING_LOD, + DONE, + } eLoadState; + + U32 mState; + std::string mFilename; + S32 mLod; + LLModelPreview* mPreview; + LLMatrix4 mTransform; + BOOL mFirstTransform; + LLVector3 mExtents[2]; + + std::map > mModel; + + typedef std::vector > model_list; + model_list mModelList; + + typedef std::vector model_instance_list; + + typedef std::map scene; + + scene mScene; + + LLModelLoader(std::string filename, S32 lod, LLModelPreview* preview); + + virtual void run(); + + void processElement(daeElement* element); + std::vector getMaterials(LLModel* model, domInstance_geometry* instance_geo); + LLImportMaterial profileToMaterial(domProfile_COMMON* material); + std::string getElementLabel(daeElement *element); + LLColor4 getDaeColor(daeElement* element); + + //map of avatar joints as named in COLLADA assets to internal joint names + std::map mJointMap; +}; + +class LLModelPreview : public LLViewerDynamicTexture, public LLMutex +{ + public: + + LLModelPreview(S32 width, S32 height, LLFloaterModelPreview* fmp); + virtual ~LLModelPreview(); + + void setPreviewTarget(F32 distance); + void setTexture(U32 name) { mTextureName = name; } + + BOOL render(); + void genBuffers(S32 lod); + void refresh(); + void rotate(F32 yaw_radians, F32 pitch_radians); + void zoom(F32 zoom_amt); + void pan(F32 right, F32 up); + virtual BOOL needsRender() { return mNeedsUpdate; } + void setPreviewLOD(S32 lod); + void loadModel(std::string filename, S32 lod); + void loadModelCallback(S32 lod); + void genLODs(S32 which_lod = -1); + void smoothNormals(); + void consolidate(); + void scrubMaterials(); + U32 calcResourceCost(); + void rebuildUploadData(); + void clearIncompatible(S32 lod); + void updateStatusMessages(); + + static void textureLoadedCallback( BOOL success, LLViewerFetchedTexture *src_vi, LLImageRaw* src, LLImageRaw* src_aux, S32 discard_level, BOOL final, void* userdata ); + + protected: + friend class LLFloaterModelPreview; + friend class LLPhysicsDecomp; + + LLFloaterModelPreview* mFMP; + + BOOL mNeedsUpdate; + U32 mTextureName; + F32 mCameraDistance; + F32 mCameraYaw; + F32 mCameraPitch; + F32 mCameraZoom; + LLVector3 mCameraOffset; + LLVector3 mPreviewTarget; + LLVector3 mPreviewScale; + S32 mPreviewLOD; + U32 mResourceCost; + S32 mLODMode[LLModel::NUM_LODS]; + S32 mLimit[LLModel::NUM_LODS]; + + LLModelLoader* mModelLoader; + + + LLModelLoader::scene mScene[LLModel::NUM_LODS]; + LLModelLoader::scene mBaseScene; + + LLModelLoader::model_list mModel[LLModel::NUM_LODS]; + LLModelLoader::model_list mBaseModel; + + std::map mGroup; + std::map mObject; + std::map > mPatch; + + std::map mPercentage; + std::map > > mPhysicsMesh; + + LLMeshUploadThread::instance_list mUploadData; + std::set > mTextureSet; + + //map of vertex buffers to models (one vertex buffer in vector per face in model + std::map > > mVertexBuffer[6]; +}; + +class LLFloaterModelPreview : public LLFloater +{ +public: + static LLFloaterModelPreview* sInstance; + + LLFloaterModelPreview(const LLSD& key); + virtual ~LLFloaterModelPreview(); + + virtual BOOL postBuild(); + + BOOL handleMouseDown(S32 x, S32 y, MASK mask); + BOOL handleMouseUp(S32 x, S32 y, MASK mask); + BOOL handleHover(S32 x, S32 y, MASK mask); + BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + + static void onMouseCaptureLostModelPreview(LLMouseHandler*); + static void setUploadAmount(S32 amount) { sUploadAmount = amount; } + + static void onBrowseHighLOD(void* data); + static void onBrowseMediumLOD(void* data); + static void onBrowseLowLOD(void* data); + static void onBrowseLowestLOD(void* data); + + static void onUpload(void* data); + + static void onConsolidate(void* data); + static void onScrubMaterials(void* data); + static void onDecompose(void* data); + static void onModelDecompositionComplete(LLModel* model, std::vector >& physics_mesh); + + static void refresh(LLUICtrl* ctrl, void* data); + + void updateResourceCost(); + + void loadModel(S32 lod); + +protected: + friend class LLModelPreview; + friend class LLMeshFilePicker; + friend class LLPhysicsDecomp; + friend class LLPhysicsDecompFloater; + + static void onPreviewLODCommit(LLUICtrl*,void*); + + static void onHighLODCommit(LLUICtrl*,void*); + static void onMediumLODCommit(LLUICtrl*,void*); + static void onLowLODCommit(LLUICtrl*,void*); + static void onLowestLODCommit(LLUICtrl*,void*); + static void onPhysicsLODCommit(LLUICtrl*,void*); + + static void onHighLimitCommit(LLUICtrl*,void*); + static void onMediumLimitCommit(LLUICtrl*,void*); + static void onLowLimitCommit(LLUICtrl*,void*); + static void onLowestLimitCommit(LLUICtrl*,void*); + static void onPhysicsLimitCommit(LLUICtrl*,void*); + + static void onSmoothNormalsCommit(LLUICtrl*,void*); + + static void onAutoFillCommit(LLUICtrl*,void*); + static void onShowEdgesCommit(LLUICtrl*,void*); + + static void onExplodeCommit(LLUICtrl*, void*); + + static void onPhysicsParamCommit(LLUICtrl* ctrl, void* userdata); + static void onPhysicsStageExecute(LLUICtrl* ctrl, void* userdata); + static void onPhysicsStageCancel(LLUICtrl* ctrl, void* userdata); + static void onClosePhysicsFloater(LLUICtrl* ctrl, void* userdata); + + void draw(); + static void setLODMode(S32 lod, void* userdata); + void setLODMode(S32 lod, S32 which_mode); + + static void setLimit(S32 lod, void* userdata); + void setLimit(S32 lod, S32 limit); + + void showDecompFloater(); + + LLModelPreview* mModelPreview; + + LLFloater* mDecompFloater; + + S32 mLastMouseX; + S32 mLastMouseY; + LLRect mPreviewRect; + U32 mGLName; + BOOL mLoading; + static S32 sUploadAmount; +}; + +#endif + +#endif // LL_LLFLOATERMODELPREVIEW_H diff --git a/indra/newview/llmeshreduction.cpp b/indra/newview/llmeshreduction.cpp new file mode 100644 index 0000000000..e785784a32 --- /dev/null +++ b/indra/newview/llmeshreduction.cpp @@ -0,0 +1,291 @@ +/** + * @file llmeshreduction.cpp + * @brief LLMeshReduction class implementation + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmeshreduction.h" +#include "llgl.h" +#include "llvertexbuffer.h" + +#include "glod/glod.h" + +#if LL_MESH_ENABLED + +static BOOL stop_gloderror() +{ + GLuint error = glodGetError(); + + if (error != GLOD_NO_ERROR) + { + llwarns << "GLOD error detected: " << std::hex << error << llendl; + return TRUE; + } + + return FALSE; +} + + +void create_vertex_buffers_from_model(LLModel* model, std::vector >& vertex_buffers) +{ +#if 0 //VECTORIZE THIS ? + vertex_buffers.clear(); + + for (S32 i = 0; i < model->getNumVolumeFaces(); ++i) + { + const LLVolumeFace &vf = model->getVolumeFace(i); + U32 num_vertices = vf.mNumVertices; + U32 num_indices = vf.mNumIndices; + + if (!num_vertices || ! num_indices) + { + continue; + } + + LLVertexBuffer* vb = + new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0, 0); + + vb->allocateBuffer(num_vertices, num_indices, TRUE); + + LLStrider vertex_strider; + LLStrider normal_strider; + LLStrider tc_strider; + LLStrider index_strider; + + vb->getVertexStrider(vertex_strider); + vb->getNormalStrider(normal_strider); + vb->getTexCoord0Strider(tc_strider); + + vb->getIndexStrider(index_strider); + + // build vertices and normals + for (U32 i = 0; (S32)i < num_vertices; i++) + { + *(vertex_strider++) = vf.mVertices[i].mPosition; + *(tc_strider++) = vf.mVertices[i].mTexCoord; + LLVector3 normal = vf.mVertices[i].mNormal; + normal.normalize(); + *(normal_strider++) = normal; + } + + // build indices + for (U32 i = 0; i < num_indices; i++) + { + *(index_strider++) = vf.mIndices[i]; + } + + + vertex_buffers.push_back(vb); + } +#endif +} + +void create_glod_object_from_vertex_buffers(S32 object, S32 group, std::vector >& vertex_buffers) +{ + glodNewGroup(group); + stop_gloderror(); + glodNewObject(object, group, GLOD_DISCRETE); + stop_gloderror(); + + for (U32 i = 0; i < vertex_buffers.size(); ++i) + { + vertex_buffers[i]->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + + U32 num_indices = vertex_buffers[i]->getNumIndices(); + + if (num_indices > 2) + { + glodInsertElements(object, i, GL_TRIANGLES, num_indices, GL_UNSIGNED_SHORT, + vertex_buffers[i]->getIndicesPointer(), 0, 0.f); + } + stop_gloderror(); + } + + glodBuildObject(object); + stop_gloderror(); +} + +// extract the GLOD data into vertex buffers +void create_vertex_buffers_from_glod_object(S32 object, S32 group, std::vector >& vertex_buffers) +{ + vertex_buffers.clear(); + + GLint patch_count = 0; + glodGetObjectParameteriv(object, GLOD_NUM_PATCHES, &patch_count); + stop_gloderror(); + + GLint* sizes = new GLint[patch_count*2]; + glodGetObjectParameteriv(object, GLOD_PATCH_SIZES, sizes); + stop_gloderror(); + + GLint* names = new GLint[patch_count]; + glodGetObjectParameteriv(object, GLOD_PATCH_NAMES, names); + stop_gloderror(); + + for (S32 i = 0; i < patch_count; i++) + { + LLPointer buff = + new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0, 0); + + if (sizes[i*2+1] > 0 && sizes[i*2] > 0) + { + buff->allocateBuffer(sizes[i*2+1], sizes[i*2], true); + buff->setBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL | LLVertexBuffer::MAP_TEXCOORD0); + glodFillElements(object, names[i], GL_UNSIGNED_SHORT, buff->getIndicesPointer()); + stop_gloderror(); + } + else + { + // this face was eliminated, create a dummy triangle (one vertex, 3 indices, all 0) + buff->allocateBuffer(1, 3, true); + } + + vertex_buffers.push_back(buff); + } + + glodDeleteObject(object); + stop_gloderror(); + glodDeleteGroup(group); + stop_gloderror(); + + delete [] sizes; + delete [] names; +} + + +LLPointer create_model_from_vertex_buffers(std::vector >& vertex_buffers) +{ + // extract the newly reduced mesh + + // create our output model + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + LLPointer out_model = new LLModel(volume_params, 0.f); + + out_model->setNumVolumeFaces(vertex_buffers.size()); + + // build new faces from each vertex buffer + for (GLint i = 0; i < vertex_buffers.size(); ++i) + { + LLStrider pos; + LLStrider norm; + LLStrider tc; + LLStrider index; + + vertex_buffers[i]->getVertexStrider(pos); + vertex_buffers[i]->getNormalStrider(norm); + vertex_buffers[i]->getTexCoord0Strider(tc); + vertex_buffers[i]->getIndexStrider(index); + + out_model->setVolumeFaceData(i, pos, norm, tc, index, + vertex_buffers[i]->getNumVerts(), vertex_buffers[i]->getNumIndices()); + } + + return out_model; +} + + + +LLMeshReduction::LLMeshReduction() +{ + mCounter = 1; + + glodInit(); +} + +LLMeshReduction::~LLMeshReduction() +{ + glodShutdown(); +} + + +LLPointer LLMeshReduction::reduce(LLModel* in_model, F32 limit, S32 mode) +{ + LLVertexBuffer::unbind(); + + // create vertex buffers from model + std::vector > in_vertex_buffers; + create_vertex_buffers_from_model(in_model, in_vertex_buffers); + + // create glod object from vertex buffers + stop_gloderror(); + S32 glod_group = mCounter++; + S32 glod_object = mCounter++; + create_glod_object_from_vertex_buffers(glod_object, glod_group, in_vertex_buffers); + + + // set reduction parameters + stop_gloderror(); + + if (mode == TRIANGLE_BUDGET) + { + // triangle budget mode + glodGroupParameteri(glod_group, GLOD_ADAPT_MODE, GLOD_TRIANGLE_BUDGET); + stop_gloderror(); + glodGroupParameteri(glod_group, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + stop_gloderror(); + S32 triangle_count = (S32)limit; + glodGroupParameteri(glod_group, GLOD_MAX_TRIANGLES, triangle_count); + stop_gloderror(); + } + else if (mode == ERROR_THRESHOLD) + { + // error threshold mode + glodGroupParameteri(glod_group, GLOD_ADAPT_MODE, GLOD_ERROR_THRESHOLD); + glodGroupParameteri(glod_group, GLOD_ERROR_MODE, GLOD_OBJECT_SPACE_ERROR); + F32 error_threshold = limit; + glodGroupParameterf(glod_group, GLOD_OBJECT_SPACE_ERROR_THRESHOLD, error_threshold); + stop_gloderror(); + } + + else + { + // not a legal mode + return NULL; + } + + + // do the reduction + glodAdaptGroup(glod_group); + stop_gloderror(); + + // convert glod object into vertex buffers + std::vector > out_vertex_buffers; + create_vertex_buffers_from_glod_object(glod_object, glod_group, out_vertex_buffers); + + // convert vertex buffers into a model + LLPointer out_model = create_model_from_vertex_buffers(out_vertex_buffers); + + + return out_model; +} + +#endif + diff --git a/indra/newview/llmeshreduction.h b/indra/newview/llmeshreduction.h new file mode 100644 index 0000000000..d86696978d --- /dev/null +++ b/indra/newview/llmeshreduction.h @@ -0,0 +1,59 @@ +/** + * @file llmeshreduction.h + * @brief LLMeshReduction class definition + * + * $LicenseInfo:firstyear=2004&license=viewergpl$ + * + * Copyright (c) 2004-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_LLMESHREDUCTION_H +#define LL_LLMESHREDUCTION_H + +#include "llmodel.h" + +#if LL_MESH_ENABLED + +class LLMeshReduction +{ + public: + enum EReductionMode + { + TRIANGLE_BUDGET, + ERROR_THRESHOLD + }; + + LLMeshReduction(); + ~LLMeshReduction(); + + LLPointer reduce(LLModel* in_model, F32 limit, S32 mode); + +private: + U32 mCounter; +}; + +#endif + +#endif // LL_LLMESHREDUCTION_H diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp new file mode 100644 index 0000000000..96a170ef07 --- /dev/null +++ b/indra/newview/llmeshrepository.cpp @@ -0,0 +1,2676 @@ +/** + * @file llmeshrepository.cpp + * @brief Mesh repository implementation. + * + * $LicenseInfo:firstyear=2005&license=viewergpl$ + * + * Copyright (c) 2005-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "apr_pools.h" +#include "apr_dso.h" + +#include "llmeshrepository.h" + +#include "llagent.h" +#include "llappviewer.h" +#include "llbufferstream.h" +#include "llcurl.h" +#include "llfasttimer.h" +#include "llfloatermodelpreview.h" +#include "llfloaterperms.h" +#include "lleconomy.h" +#include "llimagej2c.h" +#include "llhost.h" +#include "llnotificationsutil.h" +#include "llsd.h" +#include "llsdutil_math.h" +#include "llsdserialize.h" +#include "llthread.h" +#include "llvfile.h" +#include "llviewercontrol.h" +#include "llviewermenufile.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llviewertexturelist.h" +#include "llvolume.h" +#include "llvolumemgr.h" +#include "llvovolume.h" +#include "llworld.h" +#include "material_codes.h" +#include "pipeline.h" + + +#include + +#if LL_MESH_ENABLED + +LLFastTimer::DeclareTimer FTM_MESH_UPDATE("Mesh Update"); +LLFastTimer::DeclareTimer FTM_LOAD_MESH("Load Mesh"); + +LLMeshRepository gMeshRepo; + +const U32 MAX_MESH_REQUESTS_PER_SECOND = 100; + +U32 LLMeshRepository::sBytesReceived = 0; +U32 LLMeshRepository::sHTTPRequestCount = 0; +U32 LLMeshRepository::sHTTPRetryCount = 0; +U32 LLMeshRepository::sCacheBytesRead = 0; +U32 LLMeshRepository::sCacheBytesWritten = 0; +U32 LLMeshRepository::sPeakKbps = 0; + + +std::string header_lod[] = +{ + "lowest_lod", + "low_lod", + "medium_lod", + "high_lod" +}; + + +//get the number of bytes resident in memory for given volume +U32 get_volume_memory_size(const LLVolume* volume) +{ + U32 indices = 0; + U32 vertices = 0; + + for (U32 i = 0; i < volume->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = volume->getVolumeFace(i); + indices += face.mNumIndices; + vertices += face.mNumVertices; + } + + + return indices*2+vertices*11+sizeof(LLVolume)+sizeof(LLVolumeFace)*volume->getNumVolumeFaces(); +} + +std::string scrub_host_name(std::string http_url, const LLHost& host) +{ //curl loves to abuse the DNS cache, so scrub host names out of urls where trivial to prevent DNS timeouts + std::string ip_string = host.getIPString(); + std::string host_string = host.getHostName(); + + std::string::size_type idx = http_url.find(host_string); + + if (!ip_string.empty() && !host_string.empty() && idx != std::string::npos) + { + http_url.replace(idx, host_string.length(), ip_string); + } + + return http_url; +} + +LLVertexBuffer* get_vertex_buffer_from_mesh(LLCDMeshData& mesh, F32 scale = 1.f) +{ + LLVertexBuffer* buff = new LLVertexBuffer(LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_NORMAL, 0); + buff->allocateBuffer(mesh.mNumTriangles*3, 0, true); + + LLStrider pos; + LLStrider norm; + + buff->getVertexStrider(pos); + buff->getNormalStrider(norm); + + const F32* v = mesh.mVertexBase; + + if (mesh.mIndexType == LLCDMeshData::INT_16) + { + U16* idx = (U16*) mesh.mIndexBase; + for (S32 j = 0; j < mesh.mNumTriangles; ++j) + { + F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); + F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); + F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); + + idx = (U16*) (((U8*)idx)+mesh.mIndexStrideBytes); + + LLVector3 v0(mp0); + LLVector3 v1(mp1); + LLVector3 v2(mp2); + + LLVector3 n = (v1-v0)%(v2-v0); + n.normalize(); + + *pos++ = v0*scale; + *pos++ = v1*scale; + *pos++ = v2*scale; + + *norm++ = n; + *norm++ = n; + *norm++ = n; + } + } + else + { + U32* idx = (U32*) mesh.mIndexBase; + for (S32 j = 0; j < mesh.mNumTriangles; ++j) + { + F32* mp0 = (F32*) ((U8*)v+idx[0]*mesh.mVertexStrideBytes); + F32* mp1 = (F32*) ((U8*)v+idx[1]*mesh.mVertexStrideBytes); + F32* mp2 = (F32*) ((U8*)v+idx[2]*mesh.mVertexStrideBytes); + + idx = (U32*) (((U8*)idx)+mesh.mIndexStrideBytes); + + LLVector3 v0(mp0); + LLVector3 v1(mp1); + LLVector3 v2(mp2); + + LLVector3 n = (v1-v0)%(v2-v0); + n.normalize(); + + *(pos++) = v0*scale; + *(pos++) = v1*scale; + *(pos++) = v2*scale; + + *(norm++) = n; + *(norm++) = n; + *(norm++) = n; + } + } + + return buff; +} + +S32 LLMeshRepoThread::sActiveHeaderRequests = 0; +S32 LLMeshRepoThread::sActiveLODRequests = 0; +U32 LLMeshRepoThread::sMaxConcurrentRequests = 1; + + +class LLTextureCostResponder : public LLCurl::Responder +{ +public: + LLTextureUploadData mData; + LLMeshUploadThread* mThread; + + LLTextureCostResponder(LLTextureUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingConfirmations--; + if (isGoodStatus(status)) + { + mThread->priceResult(mData, content); + } + else + { + llwarns << status << ": " << reason << llendl; + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + + if (status == 499) + { + mThread->uploadTexture(mData); + } + else + { + llerrs << "Unhandled status " << status << llendl; + } + } + } +}; + +class LLTextureUploadResponder : public LLCurl::Responder +{ +public: + LLTextureUploadData mData; + LLMeshUploadThread* mThread; + + LLTextureUploadResponder(LLTextureUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingUploads--; + if (isGoodStatus(status)) + { + mData.mUUID = content["new_asset"].asUUID(); + gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content)); + mThread->onTextureUploaded(mData); + } + else + { + llwarns << status << ": " << reason << llendl; + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + + if (status == 404) + { + mThread->uploadTexture(mData); + } + else if (status == 499) + { + mThread->mConfirmedTextureQ.push(mData); + } + else + { + llerrs << "Unhandled status " << status << llendl; + } + } + } +}; + +class LLMeshCostResponder : public LLCurl::Responder +{ +public: + LLMeshUploadData mData; + LLMeshUploadThread* mThread; + + LLMeshCostResponder(LLMeshUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingConfirmations--; + + if (isGoodStatus(status)) + { + mThread->priceResult(mData, content); + } + else + { + llwarns << status << ": " << reason << llendl; + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + + if (status == 499) + { + mThread->uploadModel(mData); + } + else if (status == 400) + { + llwarns << "Status 400 received from server, giving up." << llendl; + } + else + { + llerrs << "Unhandled status " << status << llendl; + } + } + } +}; + +class LLMeshUploadResponder : public LLCurl::Responder +{ +public: + LLMeshUploadData mData; + LLMeshUploadThread* mThread; + + LLMeshUploadResponder(LLMeshUploadData data, LLMeshUploadThread* thread) + : mData(data), mThread(thread) + { + } + + virtual void completed(U32 status, const std::string& reason, const LLSD& content) + { + mThread->mPendingUploads--; + if (isGoodStatus(status)) + { + mData.mUUID = content["new_asset"].asUUID(); + if (mData.mUUID.isNull()) + { + LLSD args; + std::string message = content["error"]["message"]; + std::string identifier = content["error"]["identifier"]; + std::string invalidity_identifier = content["error"]["invalidity_identifier"]; + + args["MESSAGE"] = message; + args["IDENTIFIER"] = identifier; + args["INVALIDITY_IDENTIFIER"] = invalidity_identifier; + args["LABEL"] = mData.mBaseModel->mLabel; + + gMeshRepo.uploadError(args); + } + else + { + gMeshRepo.updateInventory(LLMeshRepository::inventory_data(mData.mPostData, content)); + mThread->onModelUploaded(mData); + } + } + else + { + llwarns << status << ": " << reason << llendl; + llwarns << "Retrying. (" << ++mData.mRetries << ")" << llendl; + + if (status == 404) + { + mThread->uploadModel(mData); + } + else if (status == 499) + { + mThread->mConfirmedQ.push(mData); + } + else if (status != 500) + { //drop internal server errors on the floor, otherwise grab + llerrs << "Unhandled status " << status << llendl; + } + } + } +}; + + +class LLMeshHeaderResponder : public LLCurl::Responder +{ +public: + LLVolumeParams mMeshParams; + + LLMeshHeaderResponder(const LLVolumeParams& mesh_params) + : mMeshParams(mesh_params) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLMeshLODResponder : public LLCurl::Responder +{ +public: + LLVolumeParams mMeshParams; + S32 mLOD; + U32 mRequestedBytes; + U32 mOffset; + + LLMeshLODResponder(const LLVolumeParams& mesh_params, S32 lod, U32 offset, U32 requested_bytes) + : mMeshParams(mesh_params), mLOD(lod), mOffset(offset), mRequestedBytes(requested_bytes) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLMeshSkinInfoResponder : public LLCurl::Responder +{ +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; + + LLMeshSkinInfoResponder(const LLUUID& id, U32 offset, U32 size) + : mMeshID(id), mRequestedBytes(size), mOffset(offset) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + +class LLMeshDecompositionResponder : public LLCurl::Responder +{ +public: + LLUUID mMeshID; + U32 mRequestedBytes; + U32 mOffset; + + LLMeshDecompositionResponder(const LLUUID& id, U32 offset, U32 size) + : mMeshID(id), mRequestedBytes(size), mOffset(offset) + { + } + + virtual void completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + +}; + + +LLMeshRepoThread::LLMeshRepoThread() +: LLThread("mesh repo", NULL) +{ + mWaiting = false; + mMutex = new LLMutex(NULL); + mHeaderMutex = new LLMutex(NULL); + mSignal = new LLCondition(NULL); +} + +LLMeshRepoThread::~LLMeshRepoThread() +{ + +} + +void LLMeshRepoThread::run() +{ + mCurlRequest = new LLCurlRequest(); + LLCDResult res = LLConvexDecomposition::initThread(); + if (res != LLCD_OK) + { + llwarns << "convex decomposition unable to be loaded" << llendl; + } + + while (!LLApp::isQuitting()) + { + mWaiting = true; + mSignal->wait(); + mWaiting = false; + + if (!LLApp::isQuitting()) + { + static U32 count = 0; + + static F32 last_hundred = gFrameTimeSeconds; + + if (gFrameTimeSeconds - last_hundred > 1.f) + { //a second has gone by, clear count + last_hundred = gFrameTimeSeconds; + count = 0; + } + + // NOTE: throttling intentionally favors LOD requests over header requests + + while (!mLODReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveLODRequests < sMaxConcurrentRequests) + { + { + LLMutexLock lock(mMutex); + LODRequest req = mLODReqQ.front(); + mLODReqQ.pop(); + if (fetchMeshLOD(req.mMeshParams, req.mLOD)) + { + count++; + } + } + } + + while (!mHeaderReqQ.empty() && count < MAX_MESH_REQUESTS_PER_SECOND && sActiveHeaderRequests < sMaxConcurrentRequests) + { + { + LLMutexLock lock(mMutex); + HeaderRequest req = mHeaderReqQ.front(); + mHeaderReqQ.pop(); + if (fetchMeshHeader(req.mMeshParams)) + { + count++; + } + } + } + + { + std::set incomplete; + for (std::set::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) + { + LLUUID mesh_id = *iter; + if (!fetchMeshSkinInfo(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + mSkinRequests = incomplete; + } + + { + std::set incomplete; + for (std::set::iterator iter = mDecompositionRequests.begin(); iter != mDecompositionRequests.end(); ++iter) + { + LLUUID mesh_id = *iter; + if (!fetchMeshDecomposition(mesh_id)) + { + incomplete.insert(mesh_id); + } + } + mDecompositionRequests = incomplete; + } + + + } + + mCurlRequest->process(); + } + + res = LLConvexDecomposition::quitThread(); + if (res != LLCD_OK) + { + llwarns << "convex decomposition unable to be quit" << llendl; + } + + delete mCurlRequest; + delete mMutex; +} + +void LLMeshRepoThread::loadMeshSkinInfo(const LLUUID& mesh_id) +{ //protected by mSignal, no locking needed here + mSkinRequests.insert(mesh_id); +} + +void LLMeshRepoThread::loadMeshDecomposition(const LLUUID& mesh_id) +{ //protected by mSignal, no locking needed here + mDecompositionRequests.insert(mesh_id); +} + +void LLMeshRepoThread::loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ //protected by mSignal, no locking needed here + + mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); + if (iter != mMeshHeader.end()) + { //if we have the header, request LOD byte range + LODRequest req(mesh_params, lod); + mLODReqQ.push(req); + } + else + { + HeaderRequest req(mesh_params); + + pending_lod_map::iterator pending = mPendingLOD.find(mesh_params); + + if (pending != mPendingLOD.end()) + { //append this lod request to existing header request + pending->second.push_back(lod); + if (pending->second.size() > 4) + { + llerrs << "WTF?" << llendl; + } + } + else + { //if no header request is pending, fetch header + mHeaderReqQ.push(req); + mPendingLOD[mesh_params].push_back(lod); + } + } +} + +//static +std::string LLMeshRepoThread::constructUrl(LLUUID mesh_id) +{ + std::string http_url; + + if (gAgent.getRegion()) + { + http_url = gAgent.getRegion()->getCapability("GetMesh"); + scrub_host_name(http_url, gAgent.getRegionHost()); + } + + if (!http_url.empty()) + { + http_url += "/?mesh_id="; + http_url += mesh_id.asString().c_str(); + } + else + { + llwarns << "Current region does not have GetMesh capability! Cannot load " << mesh_id << ".mesh" << llendl; + } + + return http_url; +} + +bool LLMeshRepoThread::fetchMeshSkinInfo(const LLUUID& mesh_id) +{ //protected by mMutex + mHeaderMutex->lock(); + + if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + + U32 header_size = mMeshHeaderSize[mesh_id]; + + if (header_size > 0) + { + S32 offset = header_size + mMeshHeader[mesh_id]["skin"]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id]["skin"]["size"].asInteger(); + + mHeaderMutex->unlock(); + + if (offset >= 0 && size > 0) + { + //check VFS for mesh skin info + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + file.seek(offset); + U8* buffer = new U8[size]; + file.read(buffer, size); + + //make sure buffer isn't all 0's (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (skinInfoReceived(mesh_id, buffer, size)) + { + delete[] buffer; + return true; + } + } + + delete[] buffer; + } + + //reading from VFS failed for whatever reason, fetch from sim + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + ++sActiveLODRequests; + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, + new LLMeshSkinInfoResponder(mesh_id, offset, size)); + } + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return true; +} + +bool LLMeshRepoThread::fetchMeshDecomposition(const LLUUID& mesh_id) +{ //protected by mMutex + mHeaderMutex->lock(); + + if (mMeshHeader.find(mesh_id) == mMeshHeader.end()) + { //we have no header info for this mesh, do nothing + mHeaderMutex->unlock(); + return false; + } + + U32 header_size = mMeshHeaderSize[mesh_id]; + + if (header_size > 0) + { + S32 offset = header_size + mMeshHeader[mesh_id]["decomposition"]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id]["decomposition"]["size"].asInteger(); + + mHeaderMutex->unlock(); + + if (offset >= 0 && size > 0) + { + //check VFS for mesh skin info + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + file.seek(offset); + U8* buffer = new U8[size]; + file.read(buffer, size); + + //make sure buffer isn't all 0's (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (decompositionReceived(mesh_id, buffer, size)) + { + delete[] buffer; + return true; + } + } + + delete[] buffer; + } + + //reading from VFS failed for whatever reason, fetch from sim + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + ++sActiveLODRequests; + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, + new LLMeshDecompositionResponder(mesh_id, offset, size)); + } + } + } + else + { + mHeaderMutex->unlock(); + } + + //early out was not hit, effectively fetched + return true; +} + +bool LLMeshRepoThread::fetchMeshHeader(const LLVolumeParams& mesh_params) +{ + bool retval = false; + + { + //look for mesh in asset in vfs + LLVFile file(gVFS, mesh_params.getSculptID(), LLAssetType::AT_MESH); + + S32 size = file.getSize(); + + if (size > 0) + { + U8 buffer[1024]; + S32 bytes = llmin(size, 1024); + LLMeshRepository::sCacheBytesRead += bytes; + file.read(buffer, bytes); + if (headerReceived(mesh_params, buffer, bytes)) + { //did not do an HTTP request, return false + return false; + } + } + } + + //either cache entry doesn't exist or is corrupt, request header from simulator + + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_params.getSculptID()); + if (!http_url.empty()) + { + ++sActiveHeaderRequests; + retval = true; + //grab first 4KB if we're going to bother with a fetch. Cache will prevent future fetches if a full mesh fits + //within the first 4KB + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(http_url, headers, 0, 4096, new LLMeshHeaderResponder(mesh_params)); + } + + return retval; +} + +bool LLMeshRepoThread::fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ //protected by mMutex + mHeaderMutex->lock(); + + bool retval = false; + + LLUUID mesh_id = mesh_params.getSculptID(); + + U32 header_size = mMeshHeaderSize[mesh_id]; + + if (header_size > 0) + { + S32 offset = header_size + mMeshHeader[mesh_id][header_lod[lod]]["offset"].asInteger(); + S32 size = mMeshHeader[mesh_id][header_lod[lod]]["size"].asInteger(); + mHeaderMutex->unlock(); + if (offset >= 0 && size > 0) + { + + //check VFS for mesh asset + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH); + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesRead += size; + file.seek(offset); + U8* buffer = new U8[size]; + file.read(buffer, size); + + //make sure buffer isn't all 0's (reserved block but not written) + bool zero = true; + for (S32 i = 0; i < llmin(size, 1024) && zero; ++i) + { + zero = buffer[i] > 0 ? false : true; + } + + if (!zero) + { //attempt to parse + if (lodReceived(mesh_params, lod, buffer, size)) + { + delete[] buffer; + return false; + } + } + + delete[] buffer; + } + + //reading from VFS failed for whatever reason, fetch from sim + std::vector headers; + headers.push_back("Accept: application/octet-stream"); + + std::string http_url = constructUrl(mesh_id); + if (!http_url.empty()) + { + ++sActiveLODRequests; + retval = true; + LLMeshRepository::sHTTPRequestCount++; + mCurlRequest->getByteRange(constructUrl(mesh_id), headers, offset, size, + new LLMeshLODResponder(mesh_params, lod, offset, size)); + } + else + { + mUnavailableQ.push(LODRequest(mesh_params, lod)); + } + } + else + { + mUnavailableQ.push(LODRequest(mesh_params, lod)); + } + } + else + { + mHeaderMutex->unlock(); + } + + return retval; +} + +bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size) +{ + LLSD header; + + U32 header_size = 0; + if (data_size > 0) + { + std::string res_str((char*) data, data_size); + + std::istringstream stream(res_str); + + if (!LLSDSerialize::deserialize(header, stream, data_size)) + { + llwarns << "Mesh header parse error. Not a valid mesh asset!" << llendl; + return false; + } + + header_size = stream.tellg(); + } + else + { + header["404"] = 1; + } + + { + U32 cost = gMeshRepo.calcResourceCost(header); + + LLUUID mesh_id = mesh_params.getSculptID(); + + mHeaderMutex->lock(); + mMeshHeaderSize[mesh_id] = header_size; + mMeshHeader[mesh_id] = header; + mMeshResourceCost[mesh_id] = cost; + mHeaderMutex->unlock(); + + //check for pending requests + pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); + if (iter != mPendingLOD.end()) + { + for (U32 i = 0; i < iter->second.size(); ++i) + { + LODRequest req(mesh_params, iter->second[i]); + mLODReqQ.push(req); + } + } + mPendingLOD.erase(iter); + } + + return true; +} + +bool LLMeshRepoThread::lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size) +{ + LLVolume* volume = new LLVolume(mesh_params, LLVolumeLODGroup::getVolumeScaleFromDetail(lod)); + std::string mesh_string((char*) data, data_size); + std::istringstream stream(mesh_string); + + if (volume->unpackVolumeFaces(stream, data_size)) + { + LoadedMesh mesh(volume, mesh_params, lod); + if (volume->getNumFaces() > 0) + { + LLMutexLock lock(mMutex); + mLoadedQ.push(mesh); + return true; + } + } + + return false; +} + +bool LLMeshRepoThread::skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size) +{ + LLSD skin; + + if (data_size > 0) + { + std::string res_str((char*) data, data_size); + + std::istringstream stream(res_str); + + if (!unzip_llsd(skin, stream, data_size)) + { + llwarns << "Mesh skin info parse error. Not a valid mesh asset!" << llendl; + return false; + } + } + + { + LLMeshSkinInfo info; + info.mMeshID = mesh_id; + + if (skin.has("joint_names")) + { + for (U32 i = 0; i < skin["joint_names"].size(); ++i) + { + info.mJointNames.push_back(skin["joint_names"][i]); + } + } + + if (skin.has("inverse_bind_matrix")) + { + for (U32 i = 0; i < skin["inverse_bind_matrix"].size(); ++i) + { + LLMatrix4 mat; + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + mat.mMatrix[j][k] = skin["inverse_bind_matrix"][i][j*4+k].asReal(); + } + } + + info.mInvBindMatrix.push_back(mat); + } + } + + if (skin.has("bind_shape_matrix")) + { + for (U32 j = 0; j < 4; j++) + { + for (U32 k = 0; k < 4; k++) + { + info.mBindShapeMatrix.mMatrix[j][k] = skin["bind_shape_matrix"][j*4+k].asReal(); + } + } + } + + mSkinInfoQ.push(info); + } + + return true; +} + +bool LLMeshRepoThread::decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size) +{ + LLSD decomp; + + if (data_size > 0) + { + std::string res_str((char*) data, data_size); + + std::istringstream stream(res_str); + + if (!unzip_llsd(decomp, stream, data_size)) + { + llwarns << "Mesh decomposition parse error. Not a valid mesh asset!" << llendl; + return false; + } + } + + { + LLMeshDecomposition* d = new LLMeshDecomposition(); + d->mMeshID = mesh_id; + + // updated for const-correctness. gcc is picky about this type of thing - Nyx + const LLSD::Binary& hulls = decomp["HullList"].asBinary(); + const LLSD::Binary& position = decomp["Position"].asBinary(); + + U16* p = (U16*) &position[0]; + + d->mHull.resize(hulls.size()); + + LLVector3 min; + LLVector3 max; + LLVector3 range; + + min.setValue(decomp["Min"]); + max.setValue(decomp["Max"]); + range = max-min; + + for (U32 i = 0; i < hulls.size(); ++i) + { + U8 count = hulls[i]; + + for (U32 j = 0; j < count; ++j) + { + d->mHull[i].push_back(LLVector3( + (F32) p[0]/65535.f*range.mV[0]+min.mV[0], + (F32) p[1]/65535.f*range.mV[1]+min.mV[1], + (F32) p[2]/65535.f*range.mV[2]+min.mV[2])); + p += 3; + } + + } + + //get mesh for decomposition + for (U32 i = 0; i < d->mHull.size(); ++i) + { + LLCDHull hull; + hull.mNumVertices = d->mHull[i].size(); + hull.mVertexBase = d->mHull[i][0].mV; + hull.mVertexStrideBytes = 12; + + LLCDMeshData mesh; + LLCDResult res = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + res = LLConvexDecomposition::getInstance()->getMeshFromHull(&hull, &mesh); + } + if (res != LLCD_OK) + { + llwarns << "could not get mesh from hull from convex decomposition lib." << llendl; + return false; + } + + + d->mMesh.push_back(get_vertex_buffer_from_mesh(mesh)); + } + + mDecompositionQ.push(d); + } + + return true; +} + +LLMeshUploadThread::LLMeshUploadThread(LLMeshUploadThread::instance_list& data, LLVector3& scale, bool upload_textures) +: LLThread("mesh upload") +{ + mInstanceList = data; + mUploadTextures = upload_textures; + mMutex = new LLMutex(NULL); + mCurlRequest = NULL; + mPendingConfirmations = 0; + mPendingUploads = 0; + mPendingCost = 0; + mFinished = false; + mOrigin = gAgent.getPositionAgent(); + mHost = gAgent.getRegionHost(); + mUploadObjectAssetCapability = gAgent.getRegion()->getCapability("UploadObjectAsset"); + mNewInventoryCapability = gAgent.getRegion()->getCapability("NewFileAgentInventoryVariablePrice"); + + mOrigin += gAgent.getAtAxis() * scale.magVec(); + + scrub_host_name(mUploadObjectAssetCapability, mHost); + scrub_host_name(mNewInventoryCapability, mHost); +} + +LLMeshUploadThread::~LLMeshUploadThread() +{ + +} + +void LLMeshUploadThread::run() +{ + mCurlRequest = new LLCurlRequest(); + + //build map of LLModel refs to instances for callbacks + for (instance_list::iterator iter = mInstanceList.begin(); iter != mInstanceList.end(); ++iter) + { + mInstance[iter->mModel].push_back(*iter); + } + + std::set > textures; + + //populate upload queue with relevant models + for (instance_map::iterator iter = mInstance.begin(); iter != mInstance.end(); ++iter) + { + LLMeshUploadData data; + data.mBaseModel = iter->first; + + LLModelInstance& instance = *(iter->second.begin()); + + for (S32 i = 0; i < 5; i++) + { + data.mModel[i] = instance.mLOD[i]; + } + + uploadModel(data); + + if (mUploadTextures) + { + for (std::vector::iterator material_iter = instance.mMaterial.begin(); + material_iter != instance.mMaterial.end(); ++material_iter) + { + + if (textures.find(material_iter->mDiffuseMap) == textures.end()) + { + textures.insert(material_iter->mDiffuseMap); + + LLTextureUploadData data(material_iter->mDiffuseMap, material_iter->mDiffuseMapLabel); + uploadTexture(data); + } + } + } + } + + + //upload textures + bool done = false; + do + { + if (!mTextureQ.empty()) + { + sendCostRequest(mTextureQ.front()); + mTextureQ.pop(); + } + + if (!mConfirmedTextureQ.empty()) + { + doUploadTexture(mConfirmedTextureQ.front()); + mConfirmedTextureQ.pop(); + } + + mCurlRequest->process(); + + done = mTextureQ.empty() && mConfirmedTextureQ.empty(); + } + while (!done || mCurlRequest->getQueued() > 0); + + LLSD object_asset; + object_asset["objects"] = LLSD::emptyArray(); + + done = false; + do + { + static S32 count = 0; + static F32 last_hundred = gFrameTimeSeconds; + if (gFrameTimeSeconds - last_hundred > 1.f) + { + last_hundred = gFrameTimeSeconds; + count = 0; + } + + //how many requests to push before calling process + const S32 PUSH_PER_PROCESS = 32; + + S32 tcount = llmin(count+PUSH_PER_PROCESS, 100); + + while (!mUploadQ.empty() && count < tcount) + { //send any pending upload requests + mMutex->lock(); + LLMeshUploadData data = mUploadQ.front(); + mUploadQ.pop(); + mMutex->unlock(); + sendCostRequest(data); + count++; + } + + tcount = llmin(count+PUSH_PER_PROCESS, 100); + + while (!mConfirmedQ.empty() && count < tcount) + { //process any meshes that have been confirmed for upload + LLMeshUploadData& data = mConfirmedQ.front(); + doUploadModel(data); + mConfirmedQ.pop(); + count++; + } + + tcount = llmin(count+PUSH_PER_PROCESS, 100); + + while (!mInstanceQ.empty() && count < tcount) + { //create any objects waiting for upload + count++; + object_asset["objects"].append(createObject(mInstanceQ.front())); + mInstanceQ.pop(); + } + + mCurlRequest->process(); + + done = mInstanceQ.empty() && mConfirmedQ.empty() && mUploadQ.empty(); + } + while (!done || mCurlRequest->getQueued() > 0); + + delete mCurlRequest; + mCurlRequest = NULL; + + // now upload the object asset + std::string url = mUploadObjectAssetCapability; + LLHTTPClient::post(url, object_asset, new LLHTTPClient::Responder()); + + mFinished = true; +} + +void LLMeshUploadThread::uploadModel(LLMeshUploadData& data) +{ //called from arbitrary thread + { + LLMutexLock lock(mMutex); + mUploadQ.push(data); + } +} + +void LLMeshUploadThread::uploadTexture(LLTextureUploadData& data) +{ //called from mesh upload thread + mTextureQ.push(data); +} + + +static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_LOADED("Notify Loaded"); +static LLFastTimer::DeclareTimer FTM_NOTIFY_MESH_UNAVAILABLE("Notify Unavailable"); + +void LLMeshRepoThread::notifyLoadedMeshes() +{ + while (!mLoadedQ.empty()) + { + mMutex->lock(); + LoadedMesh mesh = mLoadedQ.front(); + mLoadedQ.pop(); + mMutex->unlock(); + + if (mesh.mVolume && mesh.mVolume->getNumVolumeFaces() > 0) + { + gMeshRepo.notifyMeshLoaded(mesh.mMeshParams, mesh.mVolume); + } + else + { + gMeshRepo.notifyMeshUnavailable(mesh.mMeshParams, + LLVolumeLODGroup::getVolumeDetailFromScale(mesh.mVolume->getDetail())); + } + } + + while (!mUnavailableQ.empty()) + { + mMutex->lock(); + LODRequest req = mUnavailableQ.front(); + mUnavailableQ.pop(); + mMutex->unlock(); + + gMeshRepo.notifyMeshUnavailable(req.mMeshParams, req.mLOD); + } + + while (!mSkinInfoQ.empty()) + { + gMeshRepo.notifySkinInfoReceived(mSkinInfoQ.front()); + mSkinInfoQ.pop(); + } + + while (!mDecompositionQ.empty()) + { + gMeshRepo.notifyDecompositionReceived(mDecompositionQ.front()); + mDecompositionQ.pop(); + } +} + +S32 LLMeshRepoThread::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ //only ever called from main thread + lod = llclamp(lod, 0, 3); + + LLMutexLock lock(mHeaderMutex); + mesh_header_map::iterator iter = mMeshHeader.find(mesh_params.getSculptID()); + + if (iter != mMeshHeader.end()) + { + LLSD& header = iter->second; + + if (header.has("404")) + { + return -1; + } + + if (header[header_lod[lod]]["size"].asInteger() > 0) + { + return lod; + } + + //search down to find the next available lower lod + for (S32 i = lod-1; i >= 0; --i) + { + if (header[header_lod[i]]["size"].asInteger() > 0) + { + return i; + } + } + + //search up to find then ext available higher lod + for (S32 i = lod+1; i < 4; ++i) + { + if (header[header_lod[i]]["size"].asInteger() > 0) + { + return i; + } + } + + //header exists and no good lod found, treat as 404 + header["404"] = 1; + return -1; + } + + return lod; +} + +U32 LLMeshRepoThread::getResourceCost(const LLUUID& mesh_id) +{ + LLMutexLock lock(mHeaderMutex); + + std::map::iterator iter = mMeshResourceCost.find(mesh_id); + if (iter != mMeshResourceCost.end()) + { + return iter->second; + } + + return 0; +} + +void LLMeshRepository::cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header) +{ + mThread->mMeshHeader[data.mUUID] = header; + + // we cache the mesh for default parameters + LLVolumeParams volume_params; + volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + volume_params.setSculptID(data.mUUID, LL_SCULPT_TYPE_MESH); + + for (U32 i = 0; i < 4; i++) + { + if (data.mModel[i].notNull()) + { + LLPointer volume = new LLVolume(volume_params, LLVolumeLODGroup::getVolumeScaleFromDetail(i)); + volume->copyVolumeFaces(data.mModel[i]); + } + } + +} + +void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + + LLMeshRepoThread::sActiveLODRequests--; + S32 data_size = buffer->countAfter(channels.in(), NULL); + + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + if (data_size < mRequestedBytes) + { + if (status == 499 || status == 503) + { //timeout or service unavailable, try again + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->loadMeshLOD(mMeshParams, mLOD); + } + else + { + llwarns << "Unhandled status " << status << llendl; + } + return; + } + + LLMeshRepository::sBytesReceived += mRequestedBytes; + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + if (gMeshRepo.mThread->lodReceived(mMeshParams, mLOD, data, data_size)) + { + //good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshParams.getSculptID(), LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + file.seek(offset); + file.write(data, size); + LLMeshRepository::sCacheBytesWritten += size; + } + } + + delete [] data; +} + +void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + S32 data_size = buffer->countAfter(channels.in(), NULL); + + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + if (data_size < mRequestedBytes) + { + if (status == 499 || status == 503) + { //timeout or service unavailable, try again + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->loadMeshSkinInfo(mMeshID); + } + else + { + llwarns << "Unhandled status " << status << llendl; + } + return; + } + + LLMeshRepository::sBytesReceived += mRequestedBytes; + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + if (gMeshRepo.mThread->skinInfoReceived(mMeshID, data, data_size)) + { + //good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } + + delete [] data; +} + +void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + S32 data_size = buffer->countAfter(channels.in(), NULL); + + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + if (data_size < mRequestedBytes) + { + if (status == 499 || status == 503) + { //timeout or service unavailable, try again + LLMeshRepository::sHTTPRetryCount++; + gMeshRepo.mThread->loadMeshDecomposition(mMeshID); + } + else + { + llwarns << "Unhandled status " << status << llendl; + } + return; + } + + LLMeshRepository::sBytesReceived += mRequestedBytes; + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + if (gMeshRepo.mThread->decompositionReceived(mMeshID, data, data_size)) + { + //good fetch from sim, write to VFS for caching + LLVFile file(gVFS, mMeshID, LLAssetType::AT_MESH, LLVFile::WRITE); + + S32 offset = mOffset; + S32 size = mRequestedBytes; + + if (file.getSize() >= offset+size) + { + LLMeshRepository::sCacheBytesWritten += size; + file.seek(offset); + file.write(data, size); + } + } + + delete [] data; +} + +void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) +{ + LLMeshRepoThread::sActiveHeaderRequests--; + if (status < 200 || status > 400) + { + llwarns << status << ": " << reason << llendl; + } + + S32 data_size = buffer->countAfter(channels.in(), NULL); + + U8* data = NULL; + + if (data_size > 0) + { + data = new U8[data_size]; + buffer->readAfter(channels.in(), NULL, data, data_size); + } + + LLMeshRepository::sBytesReceived += llmin(data_size, 4096); + + if (!gMeshRepo.mThread->headerReceived(mMeshParams, data, data_size)) + { + llwarns << "Header responder failed with status: " << status << ": " << reason << llendl; + if (status == 503 || status == 499) + { //retry + LLMeshRepository::sHTTPRetryCount++; + LLMeshRepoThread::HeaderRequest req(mMeshParams); + gMeshRepo.mThread->mHeaderReqQ.push(req); + } + } + else if (data && data_size > 0) + { + //header was successfully retrieved from sim, cache in vfs + LLUUID mesh_id = mMeshParams.getSculptID(); + LLSD header = gMeshRepo.mThread->mMeshHeader[mesh_id]; + + std::stringstream str; + + S32 lod_bytes = 0; + + for (U32 i = 0; i < LLModel::LOD_PHYSICS; ++i) + { //figure out how many bytes we'll need to reserve in the file + std::string lod_name = header_lod[i]; + lod_bytes = llmax(lod_bytes, header[lod_name]["offset"].asInteger()+header[lod_name]["size"].asInteger()); + } + + //just in case skin info or decomposition is at the end of the file (which it shouldn't be) + lod_bytes = llmax(lod_bytes, header["skin"]["offset"].asInteger() + header["skin"]["size"].asInteger()); + lod_bytes = llmax(lod_bytes, header["decomposition"]["offset"].asInteger() + header["decomposition"]["size"].asInteger()); + + S32 header_bytes = (S32) gMeshRepo.mThread->mMeshHeaderSize[mesh_id]; + S32 bytes = lod_bytes + header_bytes; + + + //it's possible for the remote asset to have more data than is needed for the local cache + //only allocate as much space in the VFS as is needed for the local cache + data_size = llmin(data_size, bytes); + + LLVFile file(gVFS, mesh_id, LLAssetType::AT_MESH, LLVFile::WRITE); + if (file.getMaxSize() >= bytes || file.setMaxSize(bytes)) + { + LLMeshRepository::sCacheBytesWritten += data_size; + + file.write((const U8*) data, data_size); + + //zero out the rest of the file + U8 block[4096]; + memset(block, 0, 4096); + + while (bytes-file.tell() > 4096) + { + file.write(block, 4096); + } + + S32 remaining = bytes-file.tell(); + + if (remaining < 0 || remaining > 4096) + { + llerrs << "Bad padding of mesh asset cache entry." << llendl; + } + + if (remaining > 0) + { + file.write(block, remaining); + } + } + } + + delete [] data; +} + + +LLMeshRepository::LLMeshRepository() +: mMeshMutex(NULL), + mMeshThreadCount(0), + mThread(NULL) +{ + +} + +void LLMeshRepository::init() +{ + mMeshMutex = new LLMutex(NULL); + + mDecompThread = new LLPhysicsDecomp(); + mDecompThread->start(); + + while (!mDecompThread->mInited) + { //wait for physics decomp thread to init + apr_sleep(100); + } + + mThread = new LLMeshRepoThread(); + mThread->start(); +} + +void LLMeshRepository::shutdown() +{ + mThread->mSignal->signal(); + + delete mThread; + mThread = NULL; + + for (U32 i = 0; i < mUploads.size(); ++i) + { + delete mUploads[i]; + } + + mUploads.clear(); + + delete mMeshMutex; + mMeshMutex = NULL; + + if (mDecompThread) + { + mDecompThread->shutdown(); + delete mDecompThread; + mDecompThread = NULL; + } +} + + +S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail) +{ + if (detail < 0 || detail > 4) + { + return detail; + } + + LLFastTimer t(FTM_LOAD_MESH); + + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + mesh_load_map::iterator iter = mLoadingMeshes[detail].find(mesh_params); + if (iter != mLoadingMeshes[detail].end()) + { //request pending for this mesh, append volume id to list + iter->second.insert(vobj->getID()); + } + else + { + //first request for this mesh + mLoadingMeshes[detail][mesh_params].insert(vobj->getID()); + mPendingRequests.push_back(LLMeshRepoThread::LODRequest(mesh_params, detail)); + } + } + + //do a quick search to see if we can't display something while we wait for this mesh to load + LLVolume* volume = vobj->getVolume(); + + if (volume) + { + if (volume->getNumVolumeFaces() == 0 && !volume->isTetrahedron()) + { + volume->makeTetrahedron(); + } + + LLVolumeParams params = volume->getParams(); + + LLVolumeLODGroup* group = LLPrimitive::getVolumeManager()->getGroup(params); + + if (group) + { + //first see what the next lowest LOD available might be + for (S32 i = detail-1; i >= 0; --i) + { + LLVolume* lod = group->refLOD(i); + if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return i; + } + + group->derefLOD(lod); + } + + //no lower LOD is a available, is a higher lod available? + for (S32 i = detail+1; i < 4; ++i) + { + LLVolume* lod = group->refLOD(i); + if (lod && !lod->isTetrahedron() && lod->getNumVolumeFaces() > 0) + { + group->derefLOD(lod); + return i; + } + + group->derefLOD(lod); + } + } + else + { + llerrs << "WTF?" << llendl; + } + } + + return detail; +} + +static LLFastTimer::DeclareTimer FTM_START_MESH_THREAD("Start Thread"); +static LLFastTimer::DeclareTimer FTM_LOAD_MESH_LOD("Load LOD"); +static LLFastTimer::DeclareTimer FTM_MESH_LOCK1("Lock 1"); +static LLFastTimer::DeclareTimer FTM_MESH_LOCK2("Lock 2"); + +void LLMeshRepository::notifyLoadedMeshes() +{ //called from main thread + + LLMeshRepoThread::sMaxConcurrentRequests = gSavedSettings.getU32("MeshMaxConcurrentRequests"); + + //clean up completed upload threads + for (std::vector::iterator iter = mUploads.begin(); iter != mUploads.end(); ) + { + LLMeshUploadThread* thread = *iter; + + if (thread->isStopped() && thread->finished()) + { + iter = mUploads.erase(iter); + delete thread; + } + else + { + ++iter; + } + } + + //update inventory + if (!mInventoryQ.empty()) + { + LLMutexLock lock(mMeshMutex); + while (!mInventoryQ.empty()) + { + inventory_data& data = mInventoryQ.front(); + + LLAssetType::EType asset_type = LLAssetType::lookup(data.mPostData["asset_type"].asString()); + LLInventoryType::EType inventory_type = LLInventoryType::lookup(data.mPostData["inventory_type"].asString()); + + on_new_single_inventory_upload_complete( + asset_type, + inventory_type, + data.mPostData["asset_type"].asString(), + data.mPostData["folder_id"].asUUID(), + data.mPostData["name"], + data.mPostData["description"], + data.mResponse, + 0); + + mInventoryQ.pop(); + } + } + + if (!mThread->mWaiting) + { //curl thread is churning, wait for it to go idle + return; + } + + LLFastTimer t(FTM_MESH_UPDATE); + + { + LLFastTimer t(FTM_MESH_LOCK1); + mMeshMutex->lock(); + } + + { + LLFastTimer t(FTM_MESH_LOCK2); + mThread->mMutex->lock(); + } + + //popup queued error messages from background threads + while (!mUploadErrorQ.empty()) + { + LLNotificationsUtil::add("MeshUploadError", mUploadErrorQ.front()); + mUploadErrorQ.pop(); + } + + S32 push_count = LLMeshRepoThread::sMaxConcurrentRequests-(LLMeshRepoThread::sActiveHeaderRequests+LLMeshRepoThread::sActiveLODRequests); + + if (push_count > 0) + { + //calculate "score" for pending requests + + //create score map + std::map score_map; + + for (U32 i = 0; i < 4; ++i) + { + for (mesh_load_map::iterator iter = mLoadingMeshes[i].begin(); iter != mLoadingMeshes[i].end(); ++iter) + { + F32 max_score = 0.f; + for (std::set::iterator obj_iter = iter->second.begin(); obj_iter != iter->second.end(); ++obj_iter) + { + LLViewerObject* object = gObjectList.findObject(*obj_iter); + + if (object) + { + LLDrawable* drawable = object->mDrawable; + if (drawable) + { + F32 cur_score = drawable->getRadius()/llmax(drawable->mDistanceWRTCamera, 1.f); + max_score = llmax(max_score, cur_score); + } + } + } + + score_map[iter->first.getSculptID()] = max_score; + } + } + + //set "score" for pending requests + for (std::vector::iterator iter = mPendingRequests.begin(); iter != mPendingRequests.end(); ++iter) + { + iter->mScore = score_map[iter->mMeshParams.getSculptID()]; + } + + //sort by "score" + std::sort(mPendingRequests.begin(), mPendingRequests.end(), LLMeshRepoThread::CompareScoreGreater()); + + while (!mPendingRequests.empty() && push_count > 0) + { + LLFastTimer t(FTM_LOAD_MESH_LOD); + LLMeshRepoThread::LODRequest& request = mPendingRequests.front(); + mThread->loadMeshLOD(request.mMeshParams, request.mLOD); + mPendingRequests.erase(mPendingRequests.begin()); + push_count--; + } + } + + //send skin info requests + while (!mPendingSkinRequests.empty()) + { + mThread->loadMeshSkinInfo(mPendingSkinRequests.front()); + mPendingSkinRequests.pop(); + } + + //send decomposition requests + while (!mPendingDecompositionRequests.empty()) + { + mThread->loadMeshDecomposition(mPendingDecompositionRequests.front()); + mPendingDecompositionRequests.pop(); + } + + mThread->notifyLoadedMeshes(); + + mThread->mMutex->unlock(); + mMeshMutex->unlock(); + + mThread->mSignal->signal(); +} + +void LLMeshRepository::notifySkinInfoReceived(LLMeshSkinInfo& info) +{ + mSkinMap[info.mMeshID] = info; + mLoadingSkins.erase(info.mMeshID); +} + +void LLMeshRepository::notifyDecompositionReceived(LLMeshDecomposition* decomp) +{ + mDecompositionMap[decomp->mMeshID] = decomp; + mLoadingDecompositions.erase(decomp->mMeshID); +} + +void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume) +{ + S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail()); + + //get list of objects waiting to be notified this mesh is loaded + mesh_load_map::iterator obj_iter = mLoadingMeshes[detail].find(mesh_params); + + if (volume && obj_iter != mLoadingMeshes[detail].end()) + { + //make sure target volume is still valid + if (volume->getNumVolumeFaces() <= 0) + { + llwarns << "Mesh loading returned empty volume." << llendl; + volume->makeTetrahedron(); + } + + { //update system volume + LLVolume* sys_volume = LLPrimitive::getVolumeManager()->refVolume(mesh_params, detail); + if (sys_volume) + { + sys_volume->copyVolumeFaces(volume); + LLPrimitive::getVolumeManager()->unrefVolume(sys_volume); + } + else + { + llwarns << "Couldn't find system volume for given mesh." << llendl; + } + } + + //notify waiting LLVOVolume instances that their requested mesh is available + for (std::set::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter) + { + LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter); + if (vobj) + { + vobj->notifyMeshLoaded(); + } + } + + mLoadingMeshes[detail].erase(mesh_params); + } +} + +void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod) +{ + //get list of objects waiting to be notified this mesh is loaded + mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params); + + F32 detail = LLVolumeLODGroup::getVolumeScaleFromDetail(lod); + + if (obj_iter != mLoadingMeshes[lod].end()) + { + for (std::set::iterator vobj_iter = obj_iter->second.begin(); vobj_iter != obj_iter->second.end(); ++vobj_iter) + { + LLVOVolume* vobj = (LLVOVolume*) gObjectList.findObject(*vobj_iter); + if (vobj) + { + LLVolume* obj_volume = vobj->getVolume(); + + if (obj_volume && + obj_volume->getDetail() == detail && + obj_volume->getParams() == mesh_params) + { //should force volume to find most appropriate LOD + vobj->setVolume(obj_volume->getParams(), lod); + } + } + } + + mLoadingMeshes[lod].erase(mesh_params); + } +} + +S32 LLMeshRepository::getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod) +{ + return mThread->getActualMeshLOD(mesh_params, lod); +} + +U32 LLMeshRepository::calcResourceCost(LLSD& header) +{ + U32 bytes = 0; + + for (U32 i = 0; i < 4; i++) + { + bytes += header[header_lod[i]]["size"].asInteger(); + } + + bytes += header["skin"]["size"].asInteger(); + + return bytes/4096 + 1; +} + +U32 LLMeshRepository::getResourceCost(const LLUUID& mesh_id) +{ + return mThread->getResourceCost(mesh_id); +} + +const LLMeshSkinInfo* LLMeshRepository::getSkinInfo(const LLUUID& mesh_id) +{ + if (mesh_id.notNull()) + { + skin_map::iterator iter = mSkinMap.find(mesh_id); + if (iter != mSkinMap.end()) + { + return &(iter->second); + } + + //no skin info known about given mesh, try to fetch it + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + std::set::iterator iter = mLoadingSkins.find(mesh_id); + if (iter == mLoadingSkins.end()) + { //no request pending for this skin info + mLoadingSkins.insert(mesh_id); + mPendingSkinRequests.push(mesh_id); + } + } + } + + return NULL; +} + +const LLMeshDecomposition* LLMeshRepository::getDecomposition(const LLUUID& mesh_id) +{ + if (mesh_id.notNull()) + { + decomposition_map::iterator iter = mDecompositionMap.find(mesh_id); + if (iter != mDecompositionMap.end()) + { + return iter->second; + } + + //no skin info known about given mesh, try to fetch it + { + LLMutexLock lock(mMeshMutex); + //add volume to list of loading meshes + std::set::iterator iter = mLoadingDecompositions.find(mesh_id); + if (iter == mLoadingDecompositions.end()) + { //no request pending for this skin info + mLoadingDecompositions.insert(mesh_id); + mPendingDecompositionRequests.push(mesh_id); + } + } + } + + return NULL; +} + +void LLMeshRepository::uploadModel(std::vector& data, LLVector3& scale, bool upload_textures) +{ + LLMeshUploadThread* thread = new LLMeshUploadThread(data, scale, upload_textures); + mUploads.push_back(thread); + thread->start(); +} + +void LLMeshUploadThread::sendCostRequest(LLMeshUploadData& data) +{ + //write model file to memory buffer + std::stringstream ostr; + + LLModel::physics_shape& phys_shape = data.mModel[LLModel::LOD_PHYSICS].notNull() ? + data.mModel[LLModel::LOD_PHYSICS]->mPhysicsShape : + data.mBaseModel->mPhysicsShape; + + LLSD header = LLModel::writeModel(ostr, + data.mModel[LLModel::LOD_PHYSICS], + data.mModel[LLModel::LOD_HIGH], + data.mModel[LLModel::LOD_MEDIUM], + data.mModel[LLModel::LOD_LOW], + data.mModel[LLModel::LOD_IMPOSTOR], + phys_shape, + true); + + std::string desc = data.mBaseModel->mLabel; + + // Grab the total vertex count of the model + // along with other information for the "asset_resources" map + // to send to the server. + LLSD asset_resources = LLSD::emptyMap(); + + + std::string url = mNewInventoryCapability; + + if (!url.empty()) + { + LLSD body = generate_new_resource_upload_capability_body( + LLAssetType::AT_MESH, + desc, + desc, + LLFolderType::FT_MESH, + LLInventoryType::IT_MESH, + LLFloaterPerms::getNextOwnerPerms(), + LLFloaterPerms::getGroupPerms(), + LLFloaterPerms::getEveryonePerms()); + + body["asset_resources"] = asset_resources; + + mPendingConfirmations++; + LLCurlRequest::headers_t headers; + + data.mPostData = body; + + mCurlRequest->post(url, headers, body, new LLMeshCostResponder(data, this)); + } +} + +void LLMeshUploadThread::sendCostRequest(LLTextureUploadData& data) +{ + if (data.mTexture.notNull() && data.mTexture->getDiscardLevel() >= 0) + { + LLSD asset_resources = LLSD::emptyMap(); + + std::string url = mNewInventoryCapability; + + if (!url.empty()) + { + LLSD body = generate_new_resource_upload_capability_body( + LLAssetType::AT_TEXTURE, + data.mLabel, + data.mLabel, + LLFolderType::FT_TEXTURE, + LLInventoryType::IT_TEXTURE, + LLFloaterPerms::getNextOwnerPerms(), + LLFloaterPerms::getGroupPerms(), + LLFloaterPerms::getEveryonePerms()); + + body["asset_resources"] = asset_resources; + + mPendingConfirmations++; + LLCurlRequest::headers_t headers; + + data.mPostData = body; + mCurlRequest->post(url, headers, body, new LLTextureCostResponder(data, this)); + } + } +} + + +void LLMeshUploadThread::doUploadModel(LLMeshUploadData& data) +{ + if (!data.mRSVP.empty()) + { + std::stringstream ostr; + + LLModel::physics_shape& phys_shape = data.mModel[LLModel::LOD_PHYSICS].notNull() ? + data.mModel[LLModel::LOD_PHYSICS]->mPhysicsShape : + data.mBaseModel->mPhysicsShape; + + LLModel::writeModel(ostr, + data.mModel[LLModel::LOD_PHYSICS], + data.mModel[LLModel::LOD_HIGH], + data.mModel[LLModel::LOD_MEDIUM], + data.mModel[LLModel::LOD_LOW], + data.mModel[LLModel::LOD_IMPOSTOR], + phys_shape); + + data.mAssetData = ostr.str(); + + LLCurlRequest::headers_t headers; + mPendingUploads++; + + mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLMeshUploadResponder(data, this)); + } +} + +void LLMeshUploadThread::doUploadTexture(LLTextureUploadData& data) +{ + if (!data.mRSVP.empty()) + { + std::stringstream ostr; + + if (!data.mTexture->isRawImageValid()) + { + data.mTexture->reloadRawImage(data.mTexture->getDiscardLevel()); + } + + LLPointer upload_file = LLViewerTextureList::convertToUploadFile(data.mTexture->getRawImage()); + + ostr.write((const char*) upload_file->getData(), upload_file->getDataSize()); + + data.mAssetData = ostr.str(); + + LLCurlRequest::headers_t headers; + mPendingUploads++; + + mCurlRequest->post(data.mRSVP, headers, data.mAssetData, new LLTextureUploadResponder(data, this)); + } +} + + +void LLMeshUploadThread::onModelUploaded(LLMeshUploadData& data) +{ + createObjects(data); +} + +void LLMeshUploadThread::onTextureUploaded(LLTextureUploadData& data) +{ + mTextureMap[data.mTexture] = data; +} + + +void LLMeshUploadThread::createObjects(LLMeshUploadData& data) +{ + instance_list& instances = mInstance[data.mBaseModel]; + + for (instance_list::iterator iter = instances.begin(); iter != instances.end(); ++iter) + { //create prims that reference given mesh + LLModelInstance& instance = *iter; + instance.mMeshID = data.mUUID; + mInstanceQ.push(instance); + } +} + +LLSD LLMeshUploadThread::createObject(LLModelInstance& instance) +{ + LLMatrix4 transformation = instance.mTransform; + + if (instance.mMeshID.isNull()) + { + llerrs << "WTF?" << llendl; + } + + // check for reflection + BOOL reflected = (transformation.determinant() < 0); + + // compute position + LLVector3 position = LLVector3(0, 0, 0) * transformation; + + // compute scale + LLVector3 x_transformed = LLVector3(1, 0, 0) * transformation - position; + LLVector3 y_transformed = LLVector3(0, 1, 0) * transformation - position; + LLVector3 z_transformed = LLVector3(0, 0, 1) * transformation - position; + F32 x_length = x_transformed.normalize(); + F32 y_length = y_transformed.normalize(); + F32 z_length = z_transformed.normalize(); + LLVector3 scale = LLVector3(x_length, y_length, z_length); + + // adjust for "reflected" geometry + LLVector3 x_transformed_reflected = x_transformed; + if (reflected) + { + x_transformed_reflected *= -1.0; + } + + // compute rotation + LLMatrix3 rotation_matrix; + rotation_matrix.setRows(x_transformed_reflected, y_transformed, z_transformed); + LLQuaternion quat_rotation = rotation_matrix.quaternion(); + quat_rotation.normalize(); // the rotation_matrix might not have been orthoginal. make it so here. + LLVector3 euler_rotation; + quat_rotation.getEulerAngles(&euler_rotation.mV[VX], &euler_rotation.mV[VY], &euler_rotation.mV[VZ]); + + // + // build parameter block to construct this prim + // + + LLSD object_params; + + // create prim + + // set volume params + U8 sculpt_type = LL_SCULPT_TYPE_MESH; + if (reflected) + { + sculpt_type |= LL_SCULPT_FLAG_MIRROR; + } + LLVolumeParams volume_params; + volume_params.setType( LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE ); + volume_params.setBeginAndEndS( 0.f, 1.f ); + volume_params.setBeginAndEndT( 0.f, 1.f ); + volume_params.setRatio ( 1, 1 ); + volume_params.setShear ( 0, 0 ); + volume_params.setSculptID(instance.mMeshID, sculpt_type); + object_params["shape"] = volume_params.asLLSD(); + + object_params["material"] = LL_MCODE_WOOD; + + object_params["group-id"] = gAgent.getGroupID(); + object_params["pos"] = ll_sd_from_vector3(position + mOrigin); + object_params["rotation"] = ll_sd_from_quaternion(quat_rotation); + object_params["scale"] = ll_sd_from_vector3(scale); + object_params["name"] = instance.mModel->mLabel; + + // load material from dae file + object_params["facelist"] = LLSD::emptyArray(); + for (S32 i = 0; i < instance.mMaterial.size(); i++) + { + LLTextureEntry te; + LLImportMaterial& mat = instance.mMaterial[i]; + + te.setColor(mat.mDiffuseColor); + + LLUUID diffuse_id = mTextureMap[mat.mDiffuseMap].mUUID; + + if (diffuse_id.notNull()) + { + te.setID(diffuse_id); + } + else + { + te.setID(LLUUID("5748decc-f629-461c-9a36-a35a221fe21f")); // blank texture + } + + te.setFullbright(mat.mFullbright); + + object_params["facelist"][i] = te.asLLSD(); + } + + // set extra parameters + LLSculptParams sculpt_params; + sculpt_params.setSculptTexture(instance.mMeshID); + sculpt_params.setSculptType(sculpt_type); + U8 buffer[MAX_OBJECT_PARAMS_SIZE+1]; + LLDataPackerBinaryBuffer dp(buffer, MAX_OBJECT_PARAMS_SIZE); + sculpt_params.pack(dp); + std::vector v(dp.getCurrentSize()); + memcpy(&v[0], buffer, dp.getCurrentSize()); + LLSD extra_parameter; + extra_parameter["extra_parameter"] = sculpt_params.mType; + extra_parameter["param_data"] = v; + object_params["extra_parameters"].append(extra_parameter); + + return object_params; +} + +void LLMeshUploadThread::priceResult(LLMeshUploadData& data, const LLSD& content) +{ + mPendingCost += content["upload_price"].asInteger(); + data.mRSVP = content["rsvp"].asString(); + data.mRSVP = scrub_host_name(data.mRSVP, mHost); + + mConfirmedQ.push(data); +} + +void LLMeshUploadThread::priceResult(LLTextureUploadData& data, const LLSD& content) +{ + mPendingCost += content["upload_price"].asInteger(); + data.mRSVP = content["rsvp"].asString(); + data.mRSVP = scrub_host_name(data.mRSVP, mHost); + + mConfirmedTextureQ.push(data); +} + + +bool LLImportMaterial::operator<(const LLImportMaterial &rhs) const +{ + if (mDiffuseMap != rhs.mDiffuseMap) + { + return mDiffuseMap < rhs.mDiffuseMap; + } + + if (mDiffuseMapFilename != rhs.mDiffuseMapFilename) + { + return mDiffuseMapFilename < rhs.mDiffuseMapFilename; + } + + if (mDiffuseMapLabel != rhs.mDiffuseMapLabel) + { + return mDiffuseMapLabel < rhs.mDiffuseMapLabel; + } + + if (mDiffuseColor != rhs.mDiffuseColor) + { + return mDiffuseColor < rhs.mDiffuseColor; + } + + return mFullbright < rhs.mFullbright; +} + + +void LLMeshRepository::updateInventory(inventory_data data) +{ + LLMutexLock lock(mMeshMutex); + mInventoryQ.push(data); +} + +void LLMeshRepository::uploadError(LLSD& args) +{ + LLMutexLock lock(mMeshMutex); + mUploadErrorQ.push(args); +} + + +LLPhysicsDecomp::LLPhysicsDecomp() +: LLThread("Physics Decomp") +{ + mInited = false; + mQuitting = false; + mDone = false; + mStage = -1; + mContinue = 1; + + mSignal = new LLCondition(NULL); + mMutex = new LLMutex(NULL); + + setStatusMessage("Idle"); +} + +LLPhysicsDecomp::~LLPhysicsDecomp() +{ + shutdown(); +} + +void LLPhysicsDecomp::shutdown() +{ + if (mSignal) + { + mQuitting = true; + mContinue = 0; + mSignal->signal(); + + while (!mDone) + { + apr_sleep(100); + } + } +} + +void LLPhysicsDecomp::setStatusMessage(std::string msg) +{ + LLMutexLock lock(mMutex); + mStatus = msg; +} + +void LLPhysicsDecomp::execute(const char* stage, LLModel* mdl) +{ + LLMutexLock lock(mMutex); + + if (mModel.notNull()) + { + llwarns << "Not done processing previous model." << llendl; + return; + } + + mModel = mdl; + //load model into LLCD + if (mdl) + { + mStage = mStageID[stage]; + + U16 index_offset = 0; + + if (mStage == 0) + { + mPositions.clear(); + mIndices.clear(); + + //queue up vertex positions and indices + for (S32 i = 0; i < mdl->getNumVolumeFaces(); ++i) + { + const LLVolumeFace& face = mdl->getVolumeFace(i); + if (mPositions.size() + face.mNumVertices > 65535) + { + continue; + } + + for (U32 j = 0; j < face.mNumVertices; ++j) + { + mPositions.push_back(LLVector3(face.mPositions[j].getF32ptr())); + } + + for (U32 j = 0; j < face.mNumIndices; ++j) + { + mIndices.push_back(face.mIndices[j]+index_offset); + } + + index_offset += face.mNumVertices; + } + } + + //signal decomposition thread + mSignal->signal(); + } +} + +//static +S32 LLPhysicsDecomp::llcdCallback(const char* status, S32 p1, S32 p2) +{ + LLPhysicsDecomp* comp = gMeshRepo.mDecompThread; + comp->setStatusMessage(llformat("%s: %d/%d", status, p1, p2)); + return comp->mContinue; +} + +void LLPhysicsDecomp::cancel() +{ + mContinue = 0; +} + +void LLPhysicsDecomp::run() +{ + LLConvexDecomposition::initSystem(); + mInited = true; + + while (!mQuitting) + { + mSignal->wait(); + if (!mQuitting) + { + //load data intoLLCD + if (mStage == 0) + { + mMesh.mVertexBase = mPositions[0].mV; + mMesh.mVertexStrideBytes = 12; + mMesh.mNumVertices = mPositions.size(); + + mMesh.mIndexType = LLCDMeshData::INT_16; + mMesh.mIndexBase = &(mIndices[0]); + mMesh.mIndexStrideBytes = 6; + + mMesh.mNumTriangles = mIndices.size()/3; + + LLCDResult ret = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + ret = LLConvexDecomposition::getInstance()->setMeshData(&mMesh); + } + + if (ret) + { + llerrs << "Convex Decomposition thread valid but could not set mesh data" << llendl; + } + } + + setStatusMessage("Executing."); + + mContinue = 1; + LLCDResult ret = LLCD_OK; + if (LLConvexDecomposition::getInstance() != NULL) + { + ret = LLConvexDecomposition::getInstance()->executeStage(mStage); + } + + mContinue = 0; + if (ret) + { + llerrs << "Convex Decomposition thread valid but could not execute stage " << mStage << llendl; + } + + + setStatusMessage("Reading results"); + + S32 num_hulls =0; + if (LLConvexDecomposition::getInstance() != NULL) + { + num_hulls = LLConvexDecomposition::getInstance()->getNumHullsFromStage(mStage); + } + + if (mModel.isNull()) + { + llerrs << "mModel should never be null here!" << llendl; + } + + mMutex->lock(); + mModel->mPhysicsShape.clear(); + mModel->mPhysicsShape.resize(num_hulls); + mModel->mHullCenter.clear(); + mModel->mHullCenter.resize(num_hulls); + std::vector > mesh_buffer; + mesh_buffer.resize(num_hulls); + mModel->mPhysicsCenter.clearVec(); + mModel->mPhysicsPoints = 0; + mMutex->unlock(); + + for (S32 i = 0; i < num_hulls; ++i) + { + std::vector p; + LLCDHull hull; + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + LLConvexDecomposition::getInstance()->getHullFromStage(mStage, i, &hull); + + const F32* v = hull.mVertexBase; + + LLVector3 hull_center; + + for (S32 j = 0; j < hull.mNumVertices; ++j) + { + LLVector3 vert(v[0], v[1], v[2]); + p.push_back(vert); + hull_center += vert; + v = (F32*) (((U8*) v) + hull.mVertexStrideBytes); + } + + + hull_center *= 1.f/hull.mNumVertices; + + LLCDMeshData mesh; + // if LLConvexDecomposition is a stub, num_hulls should have been set to 0 above, and we should not reach this code + LLConvexDecomposition::getInstance()->getMeshFromStage(mStage, i, &mesh); + + mesh_buffer[i] = get_vertex_buffer_from_mesh(mesh); + + mMutex->lock(); + mModel->mPhysicsShape[i] = p; + mModel->mHullCenter[i] = hull_center; + mModel->mPhysicsPoints += hull.mNumVertices; + mModel->mPhysicsCenter += hull_center; + + mMutex->unlock(); + } + + { + LLMutexLock lock(mMutex); + mModel->mPhysicsCenter *= 1.f/mModel->mPhysicsPoints; + + LLFloaterModelPreview::onModelDecompositionComplete(mModel, mesh_buffer); + //done updating model + mModel = NULL; + } + + setStatusMessage("Done."); + LLFloaterModelPreview::sInstance->mModelPreview->refresh(); + } + } + + LLConvexDecomposition::quitSystem(); + + //delete mSignal; + delete mMutex; + mSignal = NULL; + mMutex = NULL; + mDone = true; +} + +#endif + diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h new file mode 100644 index 0000000000..d5e21c35cc --- /dev/null +++ b/indra/newview/llmeshrepository.h @@ -0,0 +1,471 @@ +/** + * @file llmeshrepository.h + * @brief Client-side repository of mesh assets. + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + * + * Copyright (c) 2001-2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LL_MESH_REPOSITORY_H +#define LL_MESH_REPOSITORY_H + +#include "llassettype.h" +#include "llmodel.h" +#include "lluuid.h" +#include "llviewertexture.h" +#include "llvolume.h" + +#if LL_MESH_ENABLED + +#define LLCONVEXDECOMPINTER_STATIC 1 + +#include "llconvexdecomposition.h" + +class LLVOVolume; +class LLMeshResponder; +class LLCurlRequest; +class LLMutex; +class LLCondition; +class LLVFS; +class LLMeshRepository; + +class LLMeshUploadData +{ +public: + LLPointer mBaseModel; + LLPointer mModel[5]; + LLUUID mUUID; + U32 mRetries; + std::string mRSVP; + std::string mAssetData; + LLSD mPostData; + + LLMeshUploadData() + { + mRetries = 0; + } +}; + +class LLTextureUploadData +{ +public: + LLPointer mTexture; + LLUUID mUUID; + std::string mRSVP; + std::string mLabel; + U32 mRetries; + std::string mAssetData; + LLSD mPostData; + + LLTextureUploadData() + { + mRetries = 0; + } + + LLTextureUploadData(LLViewerFetchedTexture* texture, std::string& label) + : mTexture(texture), mLabel(label) + { + mRetries = 0; + } +}; + +class LLImportMaterial +{ +public: + LLPointer mDiffuseMap; + std::string mDiffuseMapFilename; + std::string mDiffuseMapLabel; + LLColor4 mDiffuseColor; + bool mFullbright; + + bool operator<(const LLImportMaterial ¶ms) const; + + LLImportMaterial() + : mFullbright(false) + { + mDiffuseColor.set(1,1,1,1); + } +}; + +class LLModelInstance +{ +public: + LLPointer mModel; + LLPointer mLOD[5]; + + LLUUID mMeshID; + + LLMatrix4 mTransform; + std::vector mMaterial; + + LLModelInstance(LLModel* model, LLMatrix4& transform, std::vector& materials) + : mModel(model), mTransform(transform), mMaterial(materials) + { + } +}; + +class LLMeshSkinInfo +{ +public: + LLUUID mMeshID; + std::vector mJointNames; + std::vector mInvBindMatrix; + LLMatrix4 mBindShapeMatrix; +}; + +class LLMeshDecomposition +{ +public: + LLMeshDecomposition() { } + + LLUUID mMeshID; + LLModel::physics_shape mHull; + + std::vector > mMesh; +}; + +class LLPhysicsDecomp : public LLThread +{ +public: + LLCondition* mSignal; + LLMutex* mMutex; + + LLCDMeshData mMesh; + + bool mInited; + bool mQuitting; + bool mDone; + + S32 mContinue; + std::string mStatus; + + std::vector mPositions; + std::vector mIndices; + + S32 mStage; + + LLPhysicsDecomp(); + ~LLPhysicsDecomp(); + + void shutdown(); + void setStatusMessage(std::string msg); + + void execute(const char* stage, LLModel* mdl); + static S32 llcdCallback(const char*, S32, S32); + void cancel(); + + virtual void run(); + + std::map mStageID; + LLPointer mModel; +}; + +class LLMeshRepoThread : public LLThread +{ +public: + + static S32 sActiveHeaderRequests; + static S32 sActiveLODRequests; + static U32 sMaxConcurrentRequests; + + LLCurlRequest* mCurlRequest; + LLMutex* mMutex; + LLMutex* mHeaderMutex; + LLCondition* mSignal; + + bool mWaiting; + + //map of known mesh headers + typedef std::map mesh_header_map; + mesh_header_map mMeshHeader; + + std::map mMeshHeaderSize; + std::map mMeshResourceCost; + + class HeaderRequest + { + public: + const LLVolumeParams mMeshParams; + + HeaderRequest(const LLVolumeParams& mesh_params) + : mMeshParams(mesh_params) + { + } + + bool operator<(const HeaderRequest& rhs) const + { + return mMeshParams < rhs.mMeshParams; + } + }; + + class LODRequest + { + public: + LLVolumeParams mMeshParams; + S32 mLOD; + F32 mScore; + + LODRequest(const LLVolumeParams& mesh_params, S32 lod) + : mMeshParams(mesh_params), mLOD(lod), mScore(0.f) + { + } + }; + + struct CompareScoreGreater + { + bool operator()(const LODRequest& lhs, const LODRequest& rhs) + { + return lhs.mScore > rhs.mScore; // greatest = first + } + }; + + + class LoadedMesh + { + public: + LLPointer mVolume; + LLVolumeParams mMeshParams; + S32 mLOD; + + LoadedMesh(LLVolume* volume, const LLVolumeParams& mesh_params, S32 lod) + : mVolume(volume), mMeshParams(mesh_params), mLOD(lod) + { + } + + }; + + //set of requested skin info + std::set mSkinRequests; + + //queue of completed skin info requests + std::queue mSkinInfoQ; + + //set of requested decompositions + std::set mDecompositionRequests; + + //queue of completed Decomposition info requests + std::queue mDecompositionQ; + + //queue of requested headers + std::queue mHeaderReqQ; + + //queue of requested LODs + std::queue mLODReqQ; + + //queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD) + std::queue mUnavailableQ; + + //queue of successfully loaded meshes + std::queue mLoadedQ; + + //map of pending header requests and currently desired LODs + typedef std::map > pending_lod_map; + pending_lod_map mPendingLOD; + + static std::string constructUrl(LLUUID mesh_id); + + LLMeshRepoThread(); + ~LLMeshRepoThread(); + + virtual void run(); + + void loadMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + bool fetchMeshHeader(const LLVolumeParams& mesh_params); + bool fetchMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + bool headerReceived(const LLVolumeParams& mesh_params, U8* data, S32 data_size); + bool lodReceived(const LLVolumeParams& mesh_params, S32 lod, U8* data, S32 data_size); + bool skinInfoReceived(const LLUUID& mesh_id, U8* data, S32 data_size); + bool decompositionReceived(const LLUUID& mesh_id, U8* data, S32 data_size); + + void notifyLoadedMeshes(); + S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + U32 getResourceCost(const LLUUID& mesh_params); + + void loadMeshSkinInfo(const LLUUID& mesh_id); + void loadMeshDecomposition(const LLUUID& mesh_id); + + //send request for skin info, returns true if header info exists + // (should hold onto mesh_id and try again later if header info does not exist) + bool fetchMeshSkinInfo(const LLUUID& mesh_id); + + //send request for decomposition, returns true if header info exists + // (should hold onto mesh_id and try again later if header info does not exist) + bool fetchMeshDecomposition(const LLUUID& mesh_id); +}; + +class LLMeshUploadThread : public LLThread +{ +public: + typedef std::vector instance_list; + instance_list mInstanceList; + + typedef std::map, instance_list> instance_map; + instance_map mInstance; + + LLMutex* mMutex; + LLCurlRequest* mCurlRequest; + S32 mPendingConfirmations; + S32 mPendingUploads; + S32 mPendingCost; + bool mFinished; + LLVector3 mOrigin; + bool mUploadTextures; + + LLHost mHost; + std::string mUploadObjectAssetCapability; + std::string mNewInventoryCapability; + + std::queue mUploadQ; + std::queue mConfirmedQ; + std::queue mInstanceQ; + + std::queue mTextureQ; + std::queue mConfirmedTextureQ; + + std::map, LLTextureUploadData> mTextureMap; + + LLMeshUploadThread(instance_list& data, LLVector3& scale, bool upload_textures); + ~LLMeshUploadThread(); + + void uploadTexture(LLTextureUploadData& data); + void doUploadTexture(LLTextureUploadData& data); + void sendCostRequest(LLTextureUploadData& data); + void priceResult(LLTextureUploadData& data, const LLSD& content); + void onTextureUploaded(LLTextureUploadData& data); + + void uploadModel(LLMeshUploadData& data); + void sendCostRequest(LLMeshUploadData& data); + void doUploadModel(LLMeshUploadData& data); + void onModelUploaded(LLMeshUploadData& data); + void createObjects(LLMeshUploadData& data); + LLSD createObject(LLModelInstance& instance); + void priceResult(LLMeshUploadData& data, const LLSD& content); + + bool finished() { return mFinished; } + virtual void run(); + +}; + +class LLMeshRepository +{ +public: + + //metrics + static U32 sBytesReceived; + static U32 sHTTPRequestCount; + static U32 sHTTPRetryCount; + static U32 sCacheBytesRead; + static U32 sCacheBytesWritten; + static U32 sPeakKbps; + + + LLMeshRepository(); + + void init(); + void shutdown(); + + //mesh management functions + S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0); + + void notifyLoadedMeshes(); + void notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume); + void notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod); + void notifySkinInfoReceived(LLMeshSkinInfo& info); + void notifyDecompositionReceived(LLMeshDecomposition* info); + + S32 getActualMeshLOD(const LLVolumeParams& mesh_params, S32 lod); + U32 calcResourceCost(LLSD& header); + U32 getResourceCost(const LLUUID& mesh_params); + const LLMeshSkinInfo* getSkinInfo(const LLUUID& mesh_id); + const LLMeshDecomposition* getDecomposition(const LLUUID& mesh_id); + + void uploadModel(std::vector& data, LLVector3& scale, bool upload_textures); + + + + + typedef std::map > mesh_load_map; + mesh_load_map mLoadingMeshes[4]; + + typedef std::map skin_map; + skin_map mSkinMap; + + typedef std::map decomposition_map; + decomposition_map mDecompositionMap; + + LLMutex* mMeshMutex; + + std::vector mPendingRequests; + + //list of mesh ids awaiting skin info + std::set mLoadingSkins; + + //list of mesh ids that need to send skin info fetch requests + std::queue mPendingSkinRequests; + + //list of mesh ids awaiting decompositions + std::set mLoadingDecompositions; + + //list of mesh ids that need to send decomposition fetch requests + std::queue mPendingDecompositionRequests; + + U32 mMeshThreadCount; + + void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header); + + LLMeshRepoThread* mThread; + std::vector mUploads; + + LLPhysicsDecomp* mDecompThread; + + class inventory_data + { + public: + LLSD mPostData; + LLSD mResponse; + + inventory_data(const LLSD& data, const LLSD& content) + : mPostData(data), mResponse(content) + { + } + }; + + std::queue mInventoryQ; + + std::queue mUploadErrorQ; + + void uploadError(LLSD& args); + void updateInventory(inventory_data data); + +}; + +extern LLMeshRepository gMeshRepo; + +#endif + +#endif + -- cgit v1.3