diff options
| author | Dave Parks <davep@lindenlab.com> | 2010-08-26 14:23:12 -0500 | 
|---|---|---|
| committer | Dave Parks <davep@lindenlab.com> | 2010-08-26 14:23:12 -0500 | 
| commit | 71de5f622a7917f78823a7e7840194e1b0f8f070 (patch) | |
| tree | 1bfa580e5262ae6a8271ade6aee08f21a019c688 /indra/newview | |
| parent | fc7a3f6daab94331aab52983caee5c68db8cd772 (diff) | |
Add missing files from viewer-experimental
Diffstat (limited to 'indra/newview')
22 files changed, 8924 insertions, 0 deletions
| 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<daeElement> > 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<daeElement> > 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<domCommon_color_or_texture_type_complexType::domColor>(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<domCommon_color_or_texture_type_complexType::domTexture>(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<domCommon_color_or_texture_type_complexType::domColor>(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<LLTextureEntry> getMaterials(LLModel* model, domInstance_geometry* instance_geo) +{ +	std::vector<LLTextureEntry> 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<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); + +		if (technique) +		{ +			daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>(); +			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<domMaterial>(instance_mat->getTarget().getElement()); +			if (material) +			{ +				domInstance_effect* instance_effect = +					daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); +				if (instance_effect) +				{ +					domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement()); +					if (effect) +					{ +						domProfile_COMMON* profile = +							daeSafeCast<domProfile_COMMON>(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<domInstance_material>(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<LLFloaterImportCollada>("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<LLModel> model = LLModel::loadModelFromDomMesh(mesh); + +	if (model->getNumVolumeFaces() == 0) +	{ +		return FALSE; +	} + +	// generate LODs +	 +	 +	std::vector<LLPointer<LLModel> > lods; +	lods.push_back(model); +	S32 triangle_count = model->getNumTriangles(); + +	for (S32 i = 0; i < 4; i++) +	{ +		LLPointer<LLModel> last_model = lods.back(); + +		S32 triangle_target = (S32)(triangle_count / pow(3.f, i + 1)); +		if (triangle_target > 16) +		{ +			LLMeshReduction reduction; +			LLPointer<LLModel> 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<LLFloaterImportCollada>("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<LLUUID, daeElement*>::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<domImage>(element); +		if (image) +		{ +			upload_started = uploadImageAsset(image); +		} +		 +		domMesh* mesh = daeSafeCast<domMesh>(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<domMesh>(element)) +		{ +			mesh_count++; +		} + +		if (daeSafeCast<domImage>(element)) +		{ +			image_count++; +		} +	} +	 +	LLFloaterReg::findTypedInstance<LLFloaterImportCollada>("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<daeElement*, LLUUID>::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<domGeometry>(instance_geo->getUrl().getElement()); +	if (!geo) +	{ +		llwarns << "cannot find geometry" << llendl; +		return; +	} +	 +	domMesh* mesh = daeSafeCast<domMesh>(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<LLTextureEntry> 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<U8> 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<LLFloaterImportCollada>("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<LLFloaterImportCollada>("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<domAsset::domUnit>(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<domAsset::domUp_axis>(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<domTranslate>(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<domRotate>(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<domScale>(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<domMatrix>(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<domInstance_geometry>(element); +	if (instance_geo) +	{ +		appendObjectAsset(instance_geo); +	} + +	domNode* node = daeSafeCast<domNode>(element); +	if (node) +	{ +		pushStack(getFirstChild(element), getElementLabel(element), mStack.front().transformation); +	} + +	domInstance_node* instance_node = daeSafeCast<domInstance_node>(element); +	if (instance_node) +	{ +		daeElement* instance = instance_node->getUrl().getElement(); +		if (instance) +		{ +			pushStack(getFirstChild(instance), getElementLabel(instance), mStack.front().transformation); +		} +	} + +	domVisual_scene* scene = daeSafeCast<domVisual_scene>(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<LLFloaterImportCollada>("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<LLImportColladaAssetCache> +{ +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<daeElement*> mUploadsPending; +	std::map<LLUUID, daeElement*> mTransactionMap; +	std::map<daeElement*, LLUUID> mAssetMap; +	 +	DAE* mDAE; +	S32 mUploads; +}; + + +class LLImportCollada  +: public LLSingleton<LLImportCollada> +{ +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<struct StackState> 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<LLSpinCtrl>(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<LLTextBox>(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<LLButton>(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<LLButton>(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<LLSliderCtrl>(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<LLSliderCtrl>(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<LLCheckBoxCtrl>(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<LLComboBox>(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<LLCheckBoxCtrl>(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<LLCheckBoxCtrl>(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<LLTextBox>(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<LLComboBox>(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<domAsset::domUnit>(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<domAsset::domUp_axis>(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<LLModel> 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<domGeometry>(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<domSource>(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<domSource>(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<domSource>(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<domSource>(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<LLModel::JointWeight> 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<LLImportMaterial> 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<domTranslate>(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<domRotate>(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<domScale>(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<domMatrix>(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<domInstance_geometry>(element); +	if (instance_geo) +	{ +		domGeometry* geo = daeSafeCast<domGeometry>(instance_geo->getUrl().getElement()); +		if (geo) +		{ +			domMesh* mesh = daeSafeCast<domMesh>(geo->getDescendant(daeElement::matchType(domMesh::ID()))); +			if (mesh) +			{ +				LLModel* model = mModel[mesh]; +				if (model) +				{ +					LLMatrix4 transformation = mTransform; + +					std::vector<LLImportMaterial> 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<domInstance_node>(element); +	if (instance_node) +	{ +		daeElement* instance = instance_node->getUrl().getElement(); +		if (instance) +		{ +			processElement(instance); +		} +	} + +	//process children +	daeTArray< daeSmartRef<daeElement> > children = element->getChildren(); +	for (S32 i = 0; i < children.getCount(); i++) +	{ +		processElement(children[i]); +	} + +	domNode* node = daeSafeCast<domNode>(element); +	if (node) +	{ //this element was a node, restore transform before processiing siblings +		mTransform = saved_transform;	 +	} +} + +std::vector<LLImportMaterial> LLModelLoader::getMaterials(LLModel* model, domInstance_geometry* instance_geo) +{ +	std::vector<LLImportMaterial> materials; +	for (int i = 0; i < model->mMaterialList.size(); i++) +	{ +		LLImportMaterial import_material; + +		domInstance_material* instance_mat = NULL; + +		domBind_material::domTechnique_common* technique = +			daeSafeCast<domBind_material::domTechnique_common>(instance_geo->getDescendant(daeElement::matchType(domBind_material::domTechnique_common::ID()))); + +		if (technique) +		{ +			daeTArray< daeSmartRef<domInstance_material> > inst_materials = technique->getChildrenByType<domInstance_material>(); +			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<domMaterial>(instance_mat->getTarget().getElement()); +			if (material) +			{ +				domInstance_effect* instance_effect = +					daeSafeCast<domInstance_effect>(material->getDescendant(daeElement::matchType(domInstance_effect::ID()))); +				if (instance_effect) +				{ +					domEffect* effect = daeSafeCast<domEffect>(instance_effect->getUrl().getElement()); +					if (effect) +					{ +						domProfile_COMMON* profile = +							daeSafeCast<domProfile_COMMON>(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<domCommon_color_or_texture_type_complexType::domTexture>(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<domImage>(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<domCommon_color_or_texture_type_complexType::domColor>(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<domCommon_color_or_texture_type_complexType::domColor>(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<LLModel*> 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<LLModel*, U32>::iterator iter = mGroup.begin(); iter != mGroup.end(); ++iter) +		{ +			glodDeleteGroup(iter->second); +			stop_gloderror(); +		} + +		for (std::map<LLModel*, U32>::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<LLImportMaterial, std::vector<LLModelInstance> > 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<LLImportMaterial> 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<LLModelInstance> instance_list; +	 +	LLVolumeParams volume_params; +	volume_params.setType(LL_PCODE_PROFILE_SQUARE, LL_PCODE_PATH_LINE); + +	std::vector<LLImportMaterial> 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<LLImportMaterial, std::vector<LLModelInstance> >::iterator iter = composite.begin(); +			iter != composite.end(); +			++iter) +	{ +		std::map<LLImportMaterial, std::vector<LLModelInstance> >::iterator next_iter = iter; ++next_iter; +		 +		for (std::vector<LLModelInstance>::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<LLModelInstance>::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<LLVertexBuffer> 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<LLVector3> pos; +				LLStrider<LLVector3> norm; +				LLStrider<LLVector2> tc; +				LLStrider<U16> 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<S32> tris[LLModel::NUM_LODS]; +	std::vector<S32> verts[LLModel::NUM_LODS]; +	std::vector<S32> 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<LLVector3> vertex_strider; +			LLStrider<LLVector3> normal_strider; +			LLStrider<LLVector2> tc_strider; +			LLStrider<U16> index_strider; +			LLStrider<LLVector4> 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<LLSpinCtrl>(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<LLModel*, std::vector<LLPointer<LLVertexBuffer> > >::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<LLColor4U> 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<LLVector3> position; +							buffer->getVertexStrider(position); +							 +							LLStrider<LLVector4> 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<LLPointer<LLVertexBuffer> >& 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<daeElement*, LLPointer<LLModel> > mModel; +	 +	typedef std::vector<LLPointer<LLModel> > model_list; +	model_list mModelList; + +	typedef std::vector<LLModelInstance> model_instance_list; +	 +	typedef std::map<LLMatrix4, model_instance_list > scene; + +	scene mScene; + +	LLModelLoader(std::string filename, S32 lod, LLModelPreview* preview); + +	virtual void run(); +	 +	void processElement(daeElement* element); +	std::vector<LLImportMaterial> 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<std::string, std::string> 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<LLModel*, U32> mGroup; +	std::map<LLModel*, U32> mObject; +	std::map<LLModel*, std::vector<U32> > mPatch; + +	std::map<LLModel*, F32> mPercentage; +	std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > mPhysicsMesh; + +	LLMeshUploadThread::instance_list mUploadData; +	std::set<LLPointer<LLViewerFetchedTexture> > mTextureSet; + +	//map of vertex buffers to models (one vertex buffer in vector per face in model +	std::map<LLModel*, std::vector<LLPointer<LLVertexBuffer> > > 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<LLPointer<LLVertexBuffer> >& 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<LLPointer <LLVertexBuffer> >& 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<LLVector3> vertex_strider; +		LLStrider<LLVector3> normal_strider; +		LLStrider<LLVector2> tc_strider; +		LLStrider<U16> 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<LLPointer <LLVertexBuffer> >& 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<LLPointer <LLVertexBuffer> >& 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<LLVertexBuffer> 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<LLModel> create_model_from_vertex_buffers(std::vector<LLPointer <LLVertexBuffer> >& 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<LLModel> 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<LLVector3> pos; +		LLStrider<LLVector3> norm; +		LLStrider<LLVector2> tc; +		LLStrider<U16> 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<LLModel> LLMeshReduction::reduce(LLModel* in_model, F32 limit, S32 mode) +{ +	LLVertexBuffer::unbind(); + +	// create vertex buffers from model +	std::vector<LLPointer<LLVertexBuffer> > 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<LLPointer<LLVertexBuffer> > out_vertex_buffers; +	create_vertex_buffers_from_glod_object(glod_object, glod_group, out_vertex_buffers); + +	// convert vertex buffers into a model +	LLPointer<LLModel> 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<LLModel> 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 <queue> + +#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<LLVector3> pos; +	LLStrider<LLVector3> 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<LLUUID> incomplete; +				for (std::set<LLUUID>::iterator iter = mSkinRequests.begin(); iter != mSkinRequests.end(); ++iter) +				{ +					LLUUID mesh_id = *iter; +					if (!fetchMeshSkinInfo(mesh_id)) +					{ +						incomplete.insert(mesh_id); +					} +				} +				mSkinRequests = incomplete; +			} + +			{ +				std::set<LLUUID> incomplete; +				for (std::set<LLUUID>::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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<LLPointer<LLViewerTexture> > 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<LLImportMaterial>::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<LLUUID, U32>::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<LLVolume> 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<LLMeshUploadThread*>::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<LLUUID, F32> 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<LLUUID>::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<LLMeshRepoThread::LODRequest>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLUUID>::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<LLModelInstance>& 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<LLImageJ2C> 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<U8> 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<LLPointer<LLVertexBuffer> > 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<LLVector3> 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<LLModel> mBaseModel; +	LLPointer<LLModel> mModel[5]; +	LLUUID mUUID; +	U32 mRetries; +	std::string mRSVP; +	std::string mAssetData; +	LLSD mPostData; + +	LLMeshUploadData() +	{ +		mRetries = 0; +	} +}; + +class LLTextureUploadData +{ +public: +	LLPointer<LLViewerFetchedTexture> 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<LLViewerFetchedTexture> 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<LLModel> mModel; +	LLPointer<LLModel> mLOD[5]; +	  +	LLUUID mMeshID; + +	LLMatrix4 mTransform; +	std::vector<LLImportMaterial> mMaterial; + +	LLModelInstance(LLModel* model, LLMatrix4& transform, std::vector<LLImportMaterial>& materials) +		: mModel(model), mTransform(transform), mMaterial(materials) +	{ +	} +}; + +class LLMeshSkinInfo  +{ +public: +	LLUUID mMeshID; +	std::vector<std::string> mJointNames; +	std::vector<LLMatrix4> mInvBindMatrix; +	LLMatrix4 mBindShapeMatrix; +}; + +class LLMeshDecomposition +{ +public: +	LLMeshDecomposition() { } + +	LLUUID mMeshID; +	LLModel::physics_shape mHull; + +	std::vector<LLPointer<LLVertexBuffer> > 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<LLVector3> mPositions; +	std::vector<U16> 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<std::string, S32> mStageID; +	LLPointer<LLModel> 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<LLUUID, LLSD> mesh_header_map; +	mesh_header_map mMeshHeader; +	 +	std::map<LLUUID, U32> mMeshHeaderSize; +	std::map<LLUUID, U32> 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<LLVolume> 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<LLUUID> mSkinRequests; +	 +	//queue of completed skin info requests +	std::queue<LLMeshSkinInfo> mSkinInfoQ; + +	//set of requested decompositions +	std::set<LLUUID> mDecompositionRequests; +	 +	//queue of completed Decomposition info requests +	std::queue<LLMeshDecomposition*> mDecompositionQ; + +	//queue of requested headers +	std::queue<HeaderRequest> mHeaderReqQ; + +	//queue of requested LODs +	std::queue<LODRequest> mLODReqQ; + +	//queue of unavailable LODs (either asset doesn't exist or asset doesn't have desired LOD) +	std::queue<LODRequest> mUnavailableQ; + +	//queue of successfully loaded meshes +	std::queue<LoadedMesh> mLoadedQ; + +	//map of pending header requests and currently desired LODs +	typedef std::map<LLVolumeParams, std::vector<S32> > 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<LLModelInstance> instance_list; +	instance_list mInstanceList; + +	typedef std::map<LLPointer<LLModel>, 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<LLMeshUploadData> mUploadQ; +	std::queue<LLMeshUploadData> mConfirmedQ; +	std::queue<LLModelInstance> mInstanceQ; + +	std::queue<LLTextureUploadData> mTextureQ; +	std::queue<LLTextureUploadData> mConfirmedTextureQ; + +	std::map<LLPointer<LLViewerFetchedTexture>, 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<LLModelInstance>& data, LLVector3& scale, bool upload_textures); + +	 + + +	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map; +	mesh_load_map mLoadingMeshes[4]; +	 +	typedef std::map<LLUUID, LLMeshSkinInfo> skin_map; +	skin_map mSkinMap; + +	typedef std::map<LLUUID, LLMeshDecomposition*> decomposition_map; +	decomposition_map mDecompositionMap; + +	LLMutex*					mMeshMutex; +	 +	std::vector<LLMeshRepoThread::LODRequest> mPendingRequests; +	 +	//list of mesh ids awaiting skin info +	std::set<LLUUID> mLoadingSkins; + +	//list of mesh ids that need to send skin info fetch requests +	std::queue<LLUUID> mPendingSkinRequests; + +	//list of mesh ids awaiting decompositions +	std::set<LLUUID> mLoadingDecompositions; + +	//list of mesh ids that need to send decomposition fetch requests +	std::queue<LLUUID> mPendingDecompositionRequests; +	 +	U32 mMeshThreadCount; + +	void cacheOutgoingMesh(LLMeshUploadData& data, LLSD& header); +	 +	LLMeshRepoThread* mThread; +	std::vector<LLMeshUploadThread*> 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<inventory_data> mInventoryQ; + +	std::queue<LLSD> mUploadErrorQ; + +	void uploadError(LLSD& args); +	void updateInventory(inventory_data data); + +}; + +extern LLMeshRepository gMeshRepo; + +#endif + +#endif + | 
