Merge remote-tracking branch 'origin/master' into physics-tweaks
This commit is contained in:
commit
00bba293ad
@ -107,15 +107,26 @@ if((WIN32 AND NOT MINGW) OR APPLE)
|
|||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/libpng")
|
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/libpng")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/lib/libpng")
|
include_directories("${PROJECT_SOURCE_DIR}/lib/libpng")
|
||||||
|
|
||||||
#build jpeg library
|
|
||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/jpeglib")
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/lib/jpeglib")
|
|
||||||
|
|
||||||
set(PNG_PNG_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lib/libpng/")
|
set(PNG_PNG_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lib/libpng/")
|
||||||
set(PNG_LIBRARY png15_static)
|
set(PNG_LIBRARY png15_static)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add jpeg-turbo library
|
||||||
|
if (APPLE)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/jpeglib")
|
||||||
|
include_directories("${PROJECT_SOURCE_DIR}/lib/jpeglib")
|
||||||
set(JPEG_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lib/jpeglib/")
|
set(JPEG_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lib/jpeglib/")
|
||||||
set(JPEG_LIBRARY jpeglib)
|
set(JPEG_LIBRARY jpeglib)
|
||||||
|
else()
|
||||||
|
find_package(JPEG REQUIRED)
|
||||||
|
include_directories(${JPEG_INCLUDE_DIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT SERVER_ONLY AND NOT USE_GLES2)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/graphics_utils")
|
||||||
|
include_directories("${PROJECT_SOURCE_DIR}/lib/graphics_utils")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Build the irrlicht library
|
# Build the irrlicht library
|
||||||
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/irrlicht")
|
add_subdirectory("${PROJECT_SOURCE_DIR}/lib/irrlicht")
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/lib/irrlicht/include")
|
include_directories("${PROJECT_SOURCE_DIR}/lib/irrlicht/include")
|
||||||
@ -368,11 +379,12 @@ target_link_libraries(supertuxkart
|
|||||||
${OGGVORBIS_LIBRARIES}
|
${OGGVORBIS_LIBRARIES}
|
||||||
${OPENAL_LIBRARY}
|
${OPENAL_LIBRARY}
|
||||||
${FREETYPE_LIBRARIES}
|
${FREETYPE_LIBRARIES}
|
||||||
|
${JPEG_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT SERVER_ONLY)
|
if(NOT SERVER_ONLY)
|
||||||
if(NOT USE_GLES2)
|
if(NOT USE_GLES2)
|
||||||
target_link_libraries(supertuxkart ${OPENGL_LIBRARIES} glew)
|
target_link_libraries(supertuxkart ${OPENGL_LIBRARIES} glew graphics_utils)
|
||||||
else()
|
else()
|
||||||
target_link_libraries(supertuxkart EGL GLESv2)
|
target_link_libraries(supertuxkart EGL GLESv2)
|
||||||
endif()
|
endif()
|
||||||
|
@ -29,13 +29,16 @@ export ASSETS_PATHS="../data \
|
|||||||
export ASSETS_DIRS="library models music sfx textures"
|
export ASSETS_DIRS="library models music sfx textures"
|
||||||
|
|
||||||
export TEXTURE_SIZE=256
|
export TEXTURE_SIZE=256
|
||||||
export SOUND_QUALITY=64
|
export SOUND_QUALITY=42
|
||||||
|
export SOUND_MONO=1
|
||||||
|
export SOUND_SAMPLE=32000
|
||||||
|
|
||||||
export RUN_OPTIMIZE_SCRIPT=0
|
export RUN_OPTIMIZE_SCRIPT=0
|
||||||
export DECREASE_QUALITY=1
|
export DECREASE_QUALITY=1
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
|
export LANG=C
|
||||||
|
|
||||||
cd "`dirname "$0"`"
|
cd "`dirname "$0"`"
|
||||||
|
|
||||||
@ -191,7 +194,22 @@ convert_sound()
|
|||||||
oggdec "$FILE" -o tmp.wav
|
oggdec "$FILE" -o tmp.wav
|
||||||
|
|
||||||
if [ -s tmp.wav ]; then
|
if [ -s tmp.wav ]; then
|
||||||
oggenc --downmix -b $SOUND_QUALITY tmp.wav -o tmp.ogg
|
OGGENC_CMD=""
|
||||||
|
|
||||||
|
if [ "$SOUND_MONO" -gt 0 ]; then
|
||||||
|
OGGENC_CMD="$OGGENC_CMD --downmix"
|
||||||
|
fi
|
||||||
|
|
||||||
|
OGG_RATE=`ogginfo "$FILE" | grep "Rate: " | cut -f 2 -d " " \
|
||||||
|
| grep -o '[0-9]*'`
|
||||||
|
|
||||||
|
if [ ! -z "$OGG_RATE" ] && [ "$OGG_RATE" -gt "32000" ]; then
|
||||||
|
OGGENC_CMD="$OGGENC_CMD --resample 32000"
|
||||||
|
fi
|
||||||
|
|
||||||
|
OGGENC_CMD="$OGGENC_CMD -b $SOUND_QUALITY"
|
||||||
|
|
||||||
|
oggenc $OGGENC_CMD tmp.wav -o tmp.ogg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -s tmp.ogg ]; then
|
if [ -s tmp.ogg ]; then
|
||||||
|
@ -147,16 +147,8 @@
|
|||||||
<spacer width="10" height="10"/>
|
<spacer width="10" height="10"/>
|
||||||
<label text="Texture compression" I18N="Video settings"/>
|
<label text="Texture compression" I18N="Video settings"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<spacer height="4" width="10" />
|
|
||||||
|
|
||||||
<div layout="horizontal-row" proportion="1" height="fit">
|
|
||||||
<checkbox id="hd-textures"/>
|
|
||||||
<spacer width="10" height="10"/>
|
|
||||||
<label text="Use high definition textures" I18N="Video settings"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<spacer height="20" width="10" />
|
<spacer height="20" width="10" />
|
||||||
|
|
||||||
<div layout="horizontal-row" width="100%" proportion="1">
|
<div layout="horizontal-row" width="100%" proportion="1">
|
||||||
@ -168,9 +160,9 @@
|
|||||||
<spacer height="4" width="10" />
|
<spacer height="4" width="10" />
|
||||||
|
|
||||||
<div layout="horizontal-row" width="100%" proportion="1">
|
<div layout="horizontal-row" width="100%" proportion="1">
|
||||||
<label text="Texture filtering" I18N="Video settings" width="40%"/>
|
<label text="Rendered image quality" I18N="Video settings" width="40%"/>
|
||||||
<spacer width="10" height="10"/>
|
<spacer width="10" height="10"/>
|
||||||
<gauge id="filtering" min_value="0" max_value="5" width="50%" />
|
<gauge id="image_quality" min_value="0" max_value="3" width="50%" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<spacer height="4" width="10" />
|
<spacer height="4" width="10" />
|
||||||
|
@ -40,7 +40,7 @@ void main(void)
|
|||||||
col = vec4(new_color.r, new_color.g, new_color.b, col.a);
|
col = vec4(new_color.r, new_color.g, new_color.b, col.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GL_ES
|
#if defined(GL_ES) && !defined(Advanced_Lighting_Enabled)
|
||||||
col.xyz *= color.xyz;
|
col.xyz *= color.xyz;
|
||||||
#else
|
#else
|
||||||
col.xyz *= pow(color.xyz, vec3(2.2));
|
col.xyz *= pow(color.xyz, vec3(2.2));
|
||||||
|
@ -16,7 +16,13 @@ void main(void)
|
|||||||
col.xyz = pow(col.xyz, vec3(2.2));
|
col.xyz = pow(col.xyz, vec3(2.2));
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
col.xyz *= pow(color.xyz, vec3(2.2));
|
|
||||||
if (col.a < 0.5) discard;
|
if (col.a < 0.5) discard;
|
||||||
|
|
||||||
|
#if defined(GL_ES) && !defined(Advanced_Lighting_Enabled)
|
||||||
|
col.xyz *= color.xyz;
|
||||||
|
#else
|
||||||
|
col.xyz *= pow(color.xyz, vec3(2.2));
|
||||||
|
#endif
|
||||||
|
|
||||||
FragColor = vec4(col.xyz, 1.);
|
FragColor = vec4(col.xyz, 1.);
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,13 @@ void main(void)
|
|||||||
col.xyz = pow(col.xyz, vec3(2.2));
|
col.xyz = pow(col.xyz, vec3(2.2));
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
col.xyz *= pow(color.xyz, vec3(2.2));
|
|
||||||
if (col.a * color.a < 0.5) discard;
|
if (col.a * color.a < 0.5) discard;
|
||||||
|
|
||||||
|
#if defined(GL_ES) && !defined(Advanced_Lighting_Enabled)
|
||||||
|
col.xyz *= color.xyz;
|
||||||
|
#else
|
||||||
|
col.xyz *= pow(color.xyz, vec3(2.2));
|
||||||
|
#endif
|
||||||
|
|
||||||
float mask = texture(colorization_mask, uv).a;
|
float mask = texture(colorization_mask, uv).a;
|
||||||
if (color_change.x > 0.0)
|
if (color_change.x > 0.0)
|
||||||
|
@ -13,7 +13,9 @@ void main()
|
|||||||
|
|
||||||
// Uncharted2 tonemap with Auria's custom coefficients
|
// Uncharted2 tonemap with Auria's custom coefficients
|
||||||
vec4 perChannel = (col * (6.9 * col + .5)) / (col * (5.2 * col + 1.7) + 0.06);
|
vec4 perChannel = (col * (6.9 * col + .5)) / (col * (5.2 * col + 1.7) + 0.06);
|
||||||
|
#if !(defined(GL_ES) && defined(Advanced_Lighting_Enabled))
|
||||||
perChannel = pow(perChannel, vec4(2.2));
|
perChannel = pow(perChannel, vec4(2.2));
|
||||||
|
#endif
|
||||||
|
|
||||||
vec2 inside = uv - 0.5;
|
vec2 inside = uv - 0.5;
|
||||||
float vignette = 1. - dot(inside, inside) * vignette_weight;
|
float vignette = 1. - dot(inside, inside) * vignette_weight;
|
||||||
|
@ -14,10 +14,6 @@ vec3 getLightFactor(vec3 diffuseMatColor, vec3 specularMatColor, float specMapVa
|
|||||||
vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz;
|
vec3 DiffuseComponent = texture(DiffuseMap, tc).xyz;
|
||||||
vec3 SpecularComponent = texture(SpecularMap, tc).xyz;
|
vec3 SpecularComponent = texture(SpecularMap, tc).xyz;
|
||||||
float ao = texture(SSAO, tc).x;
|
float ao = texture(SSAO, tc).x;
|
||||||
#ifdef GL_ES
|
|
||||||
DiffuseComponent = pow(DiffuseComponent, vec3(1. / 2.2));
|
|
||||||
SpecularComponent = pow(SpecularComponent, vec3(1. / 2.2));
|
|
||||||
#endif
|
|
||||||
vec3 tmp = diffuseMatColor * DiffuseComponent * (1. - specMapValue) + specularMatColor * SpecularComponent * specMapValue;
|
vec3 tmp = diffuseMatColor * DiffuseComponent * (1. - specMapValue) + specularMatColor * SpecularComponent * specMapValue;
|
||||||
vec3 emitCol = diffuseMatColor.xyz * diffuseMatColor.xyz * diffuseMatColor.xyz * 15.;
|
vec3 emitCol = diffuseMatColor.xyz * diffuseMatColor.xyz * diffuseMatColor.xyz * 15.;
|
||||||
return tmp * ao + (emitMapValue * emitCol);
|
return tmp * ao + (emitMapValue * emitCol);
|
||||||
|
8
lib/graphics_utils/CMakeLists.txt
Normal file
8
lib/graphics_utils/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
if (UNIX OR MINGW)
|
||||||
|
add_definitions(-O3)
|
||||||
|
endif()
|
||||||
|
add_library(graphics_utils STATIC
|
||||||
|
mipmap/cpusimd.c
|
||||||
|
mipmap/imgresize.c
|
||||||
|
)
|
571
lib/graphics_utils/mipmap/cpusimd.c
Normal file
571
lib/graphics_utils/mipmap/cpusimd.c
Normal file
@ -0,0 +1,571 @@
|
|||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2016 Alexis Naveros.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The SIMD trigonometry functions are Copyright (C) 2007 Julien Pommier
|
||||||
|
* See copyright notice for simd4f_sin_ps(), simd4f_cos_ps(), simd4f_sincos_ps()
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Some functions are Copyright (C) 2008 José Fonseca
|
||||||
|
* See copyright notice for simd4f_exp2_ps(), simd4f_log2_ps(), simd4f_pow_ps()
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions developed under contract to the SURVICE Engineering Company.
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "cpusimd.h"
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE_SUPPORT
|
||||||
|
|
||||||
|
const uint32_t CPU_ALIGN16 simd4fSignMask[4] = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
|
||||||
|
const uint32_t CPU_ALIGN16 simd4fSignMaskInv[4] = { 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff };
|
||||||
|
const float CPU_ALIGN16 simd4fHalf[4] = { 0.5, 0.5, 0.5, 0.5 };
|
||||||
|
const float CPU_ALIGN16 simd4fOne[4] = { 1.0, 1.0, 1.0, 1.0 };
|
||||||
|
const float CPU_ALIGN16 simd4fTwo[4] = { 2.0, 2.0, 2.0, 2.0 };
|
||||||
|
const float CPU_ALIGN16 simd4fThree[4] = { 3.0, 3.0, 3.0, 3.0 };
|
||||||
|
const uint32_t CPU_ALIGN16 simd4uOne[4] = { 1, 1, 1, 1 };
|
||||||
|
const uint32_t CPU_ALIGN16 simd4uOneInv[4] = { ~1, ~1, ~1, ~1 };
|
||||||
|
const uint32_t CPU_ALIGN16 simd4uTwo[4] = { 2, 2, 2, 2 };
|
||||||
|
const uint32_t CPU_ALIGN16 simd4uFour[4] = { 4, 4, 4, 4 };
|
||||||
|
const float CPU_ALIGN16 simd4fQuarter[4] = { 0.25, 0.25, 0.25, 0.25 };
|
||||||
|
const float CPU_ALIGN16 simd4fPi[4] = { M_PI, M_PI, M_PI, M_PI };
|
||||||
|
const float CPU_ALIGN16 simd4fZeroOneTwoThree[4] = { 0.0, 1.0, 2.0, 3.0 };
|
||||||
|
const uint32_t CPU_ALIGN16 simd4fAlphaMask[4] = { 0x00000000, 0x00000000, 0x00000000, 0xffffffff };
|
||||||
|
const float CPU_ALIGN16 simd4f255[4] = { 255.0f, 255.0f, 255.0f, 255.0f };
|
||||||
|
const float CPU_ALIGN16 simd4f255Inv[4] = { 1.0f/255.0f, 1.0f/255.0f, 1.0f/255.0f, 1.0f/255.0f };
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE2_SUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
/* Copyright (C) 2007 Julien Pommier
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
3. This notice may not be removed or altered from any source distribution.
|
||||||
|
|
||||||
|
(this is the zlib license)
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const float CPU_ALIGN16 simd4f_cephes_FOPI[4] = { 1.27323954473516, 1.27323954473516, 1.27323954473516, 1.27323954473516 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_minus_cephes_DP1[4] = { -0.78515625, -0.78515625, -0.78515625, -0.78515625 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_minus_cephes_DP2[4] = { -2.4187564849853515625e-4, -2.4187564849853515625e-4, -2.4187564849853515625e-4, -2.4187564849853515625e-4 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_minus_cephes_DP3[4] = { -3.77489497744594108e-8, -3.77489497744594108e-8, -3.77489497744594108e-8, -3.77489497744594108e-8 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_sincof_p0[4] = { -1.9515295891E-4, -1.9515295891E-4, -1.9515295891E-4, -1.9515295891E-4 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_sincof_p1[4] = { 8.3321608736E-3, 8.3321608736E-3, 8.3321608736E-3, 8.3321608736E-3 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_sincof_p2[4] = { -1.6666654611E-1, -1.6666654611E-1, -1.6666654611E-1, -1.6666654611E-1 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_coscof_p0[4] = { 2.443315711809948E-005, 2.443315711809948E-005, 2.443315711809948E-005, 2.443315711809948E-005 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_coscof_p1[4] = { -1.388731625493765E-003, -1.388731625493765E-003, -1.388731625493765E-003, -1.388731625493765E-003 };
|
||||||
|
static const float CPU_ALIGN16 simd4f_coscof_p2[4] = { 4.166664568298827E-002, 4.166664568298827E-002, 4.166664568298827E-002, 4.166664568298827E-002 };
|
||||||
|
|
||||||
|
__m128 simd4f_sin_ps( __m128 x )
|
||||||
|
{
|
||||||
|
__m128 xmm1, xmm2, xmm3, sign_bit, y;
|
||||||
|
__m128i emm0, emm2;
|
||||||
|
|
||||||
|
xmm2 = _mm_setzero_ps();
|
||||||
|
|
||||||
|
sign_bit = x;
|
||||||
|
/* take the absolute value */
|
||||||
|
x = _mm_and_ps( x, *(__m128 *)simd4fSignMaskInv );
|
||||||
|
/* extract the sign bit (upper one) */
|
||||||
|
sign_bit = _mm_and_ps(sign_bit, *(__m128 *)simd4fSignMask);
|
||||||
|
|
||||||
|
/* scale by 4/Pi */
|
||||||
|
y = _mm_mul_ps(x, *(__m128 *)simd4f_cephes_FOPI);
|
||||||
|
|
||||||
|
/* store the integer part of y in mm0 */
|
||||||
|
emm2 = _mm_cvttps_epi32(y);
|
||||||
|
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||||
|
emm2 = _mm_add_epi32(emm2, *(__m128i*)simd4uOne);
|
||||||
|
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uOneInv);
|
||||||
|
y = _mm_cvtepi32_ps(emm2);
|
||||||
|
|
||||||
|
/* get the swap sign flag */
|
||||||
|
emm0 = _mm_and_si128(emm2, *(__m128i*)simd4uFour);
|
||||||
|
emm0 = _mm_slli_epi32(emm0, 29);
|
||||||
|
/* get the polynom selection mask
|
||||||
|
there is one polynom for 0 <= x <= Pi/4
|
||||||
|
and another one for Pi/4<x<=Pi/2
|
||||||
|
Both branches will be computed.
|
||||||
|
*/
|
||||||
|
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uTwo);
|
||||||
|
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
|
||||||
|
|
||||||
|
__m128 swap_sign_bit = _mm_castsi128_ps(emm0);
|
||||||
|
__m128 poly_mask = _mm_castsi128_ps(emm2);
|
||||||
|
sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
|
||||||
|
|
||||||
|
/* The magic pass: "Extended precision modular arithmetic"
|
||||||
|
x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
||||||
|
xmm1 = *(__m128 *)simd4f_minus_cephes_DP1;
|
||||||
|
xmm2 = *(__m128 *)simd4f_minus_cephes_DP2;
|
||||||
|
xmm3 = *(__m128 *)simd4f_minus_cephes_DP3;
|
||||||
|
xmm1 = _mm_mul_ps(y, xmm1);
|
||||||
|
xmm2 = _mm_mul_ps(y, xmm2);
|
||||||
|
xmm3 = _mm_mul_ps(y, xmm3);
|
||||||
|
x = _mm_add_ps(x, xmm1);
|
||||||
|
x = _mm_add_ps(x, xmm2);
|
||||||
|
x = _mm_add_ps(x, xmm3);
|
||||||
|
|
||||||
|
/* Evaluate the first polynom (0 <= x <= Pi/4) */
|
||||||
|
y = *(__m128 *)simd4f_coscof_p0;
|
||||||
|
__m128 z = _mm_mul_ps(x,x);
|
||||||
|
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_add_ps(y, *(__m128 *)simd4f_coscof_p1);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_add_ps(y, *(__m128 *)simd4f_coscof_p2);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
__m128 tmp = _mm_mul_ps(z, *(__m128 *)simd4fHalf);
|
||||||
|
y = _mm_sub_ps(y, tmp);
|
||||||
|
y = _mm_add_ps(y, *(__m128 *)simd4fOne);
|
||||||
|
|
||||||
|
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
|
||||||
|
|
||||||
|
__m128 y2 = *(__m128 *)simd4f_sincof_p0;
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_add_ps(y2, *(__m128 *)simd4f_sincof_p1);
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_add_ps(y2, *(__m128 *)simd4f_sincof_p2);
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_mul_ps(y2, x);
|
||||||
|
y2 = _mm_add_ps(y2, x);
|
||||||
|
|
||||||
|
/* select the correct result from the two polynoms */
|
||||||
|
xmm3 = poly_mask;
|
||||||
|
y2 = _mm_and_ps(xmm3, y2);
|
||||||
|
y = _mm_andnot_ps(xmm3, y);
|
||||||
|
y = _mm_add_ps(y,y2);
|
||||||
|
/* update the sign */
|
||||||
|
y = _mm_xor_ps(y, sign_bit);
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* almost the same as sin_ps */
|
||||||
|
__m128 simd4f_cos_ps( __m128 x )
|
||||||
|
{
|
||||||
|
__m128 xmm1, xmm2, xmm3, y;
|
||||||
|
__m128i emm0, emm2;
|
||||||
|
|
||||||
|
xmm2 = _mm_setzero_ps();
|
||||||
|
|
||||||
|
/* take the absolute value */
|
||||||
|
x = _mm_and_ps(x, *(__m128*)simd4fSignMaskInv);
|
||||||
|
|
||||||
|
/* scale by 4/Pi */
|
||||||
|
y = _mm_mul_ps(x, *(__m128*)simd4f_cephes_FOPI);
|
||||||
|
|
||||||
|
/* store the integer part of y in mm0 */
|
||||||
|
emm2 = _mm_cvttps_epi32(y);
|
||||||
|
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||||
|
emm2 = _mm_add_epi32(emm2, *(__m128i*)simd4uOne);
|
||||||
|
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uOneInv);
|
||||||
|
y = _mm_cvtepi32_ps(emm2);
|
||||||
|
|
||||||
|
emm2 = _mm_sub_epi32(emm2, *(__m128i*)simd4uTwo);
|
||||||
|
|
||||||
|
/* get the swap sign flag */
|
||||||
|
emm0 = _mm_andnot_si128(emm2, *(__m128i*)simd4uFour);
|
||||||
|
emm0 = _mm_slli_epi32(emm0, 29);
|
||||||
|
/* get the polynom selection mask */
|
||||||
|
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uTwo);
|
||||||
|
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
|
||||||
|
|
||||||
|
__m128 sign_bit = _mm_castsi128_ps(emm0);
|
||||||
|
__m128 poly_mask = _mm_castsi128_ps(emm2);
|
||||||
|
/* The magic pass: "Extended precision modular arithmetic"
|
||||||
|
x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
||||||
|
xmm1 = *(__m128*)simd4f_minus_cephes_DP1;
|
||||||
|
xmm2 = *(__m128*)simd4f_minus_cephes_DP2;
|
||||||
|
xmm3 = *(__m128*)simd4f_minus_cephes_DP3;
|
||||||
|
xmm1 = _mm_mul_ps(y, xmm1);
|
||||||
|
xmm2 = _mm_mul_ps(y, xmm2);
|
||||||
|
xmm3 = _mm_mul_ps(y, xmm3);
|
||||||
|
x = _mm_add_ps(x, xmm1);
|
||||||
|
x = _mm_add_ps(x, xmm2);
|
||||||
|
x = _mm_add_ps(x, xmm3);
|
||||||
|
|
||||||
|
/* Evaluate the first polynom (0 <= x <= Pi/4) */
|
||||||
|
y = *(__m128*)simd4f_coscof_p0;
|
||||||
|
__m128 z = _mm_mul_ps(x,x);
|
||||||
|
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p1);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p2);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
__m128 tmp = _mm_mul_ps(z, *(__m128*)simd4fHalf);
|
||||||
|
y = _mm_sub_ps(y, tmp);
|
||||||
|
y = _mm_add_ps(y, *(__m128*)simd4fOne);
|
||||||
|
|
||||||
|
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
|
||||||
|
|
||||||
|
__m128 y2 = *(__m128*)simd4f_sincof_p0;
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p1);
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p2);
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_mul_ps(y2, x);
|
||||||
|
y2 = _mm_add_ps(y2, x);
|
||||||
|
|
||||||
|
/* select the correct result from the two polynoms */
|
||||||
|
xmm3 = poly_mask;
|
||||||
|
y2 = _mm_and_ps(xmm3, y2); //, xmm3);
|
||||||
|
y = _mm_andnot_ps(xmm3, y);
|
||||||
|
y = _mm_add_ps(y,y2);
|
||||||
|
/* update the sign */
|
||||||
|
y = _mm_xor_ps(y, sign_bit);
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* since sin_ps and cos_ps are almost identical, sincos_ps could replace both of them..
|
||||||
|
it is almost as fast, and gives you a free cosine with your sine */
|
||||||
|
void simd4f_sincos_ps( __m128 x, __m128 *s, __m128 *c )
|
||||||
|
{
|
||||||
|
__m128 xmm1, xmm2, xmm3, sign_bit_sin, y;
|
||||||
|
__m128i emm0, emm2, emm4;
|
||||||
|
|
||||||
|
xmm3 = _mm_setzero_ps();
|
||||||
|
|
||||||
|
sign_bit_sin = x;
|
||||||
|
/* take the absolute value */
|
||||||
|
x = _mm_and_ps(x, *(__m128*)simd4fSignMaskInv);
|
||||||
|
/* extract the sign bit (upper one) */
|
||||||
|
sign_bit_sin = _mm_and_ps(sign_bit_sin, *(__m128*)simd4fSignMask);
|
||||||
|
|
||||||
|
/* scale by 4/Pi */
|
||||||
|
y = _mm_mul_ps(x, *(__m128*)simd4f_cephes_FOPI);
|
||||||
|
|
||||||
|
/* store the integer part of y in emm2 */
|
||||||
|
emm2 = _mm_cvttps_epi32(y);
|
||||||
|
|
||||||
|
/* j=(j+1) & (~1) (see the cephes sources) */
|
||||||
|
emm2 = _mm_add_epi32(emm2, *(__m128i*)simd4uOne);
|
||||||
|
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uOneInv);
|
||||||
|
y = _mm_cvtepi32_ps(emm2);
|
||||||
|
|
||||||
|
emm4 = emm2;
|
||||||
|
|
||||||
|
/* get the swap sign flag for the sine */
|
||||||
|
emm0 = _mm_and_si128(emm2, *(__m128i*)simd4uFour);
|
||||||
|
emm0 = _mm_slli_epi32(emm0, 29);
|
||||||
|
__m128 swap_sign_bit_sin = _mm_castsi128_ps(emm0);
|
||||||
|
|
||||||
|
/* get the polynom selection mask for the sine*/
|
||||||
|
emm2 = _mm_and_si128(emm2, *(__m128i*)simd4uTwo);
|
||||||
|
emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
|
||||||
|
__m128 poly_mask = _mm_castsi128_ps(emm2);
|
||||||
|
|
||||||
|
/* The magic pass: "Extended precision modular arithmetic"
|
||||||
|
x = ((x - y * DP1) - y * DP2) - y * DP3; */
|
||||||
|
xmm1 = *(__m128*)simd4f_minus_cephes_DP1;
|
||||||
|
xmm2 = *(__m128*)simd4f_minus_cephes_DP2;
|
||||||
|
xmm3 = *(__m128*)simd4f_minus_cephes_DP3;
|
||||||
|
xmm1 = _mm_mul_ps(y, xmm1);
|
||||||
|
xmm2 = _mm_mul_ps(y, xmm2);
|
||||||
|
xmm3 = _mm_mul_ps(y, xmm3);
|
||||||
|
x = _mm_add_ps(x, xmm1);
|
||||||
|
x = _mm_add_ps(x, xmm2);
|
||||||
|
x = _mm_add_ps(x, xmm3);
|
||||||
|
|
||||||
|
emm4 = _mm_sub_epi32(emm4, *(__m128i*)simd4uTwo);
|
||||||
|
emm4 = _mm_andnot_si128(emm4, *(__m128i*)simd4uFour);
|
||||||
|
emm4 = _mm_slli_epi32(emm4, 29);
|
||||||
|
__m128 sign_bit_cos = _mm_castsi128_ps(emm4);
|
||||||
|
|
||||||
|
sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
|
||||||
|
|
||||||
|
/* Evaluate the first polynom (0 <= x <= Pi/4) */
|
||||||
|
__m128 z = _mm_mul_ps(x,x);
|
||||||
|
y = *(__m128*)simd4f_coscof_p0;
|
||||||
|
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p1);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_add_ps(y, *(__m128*)simd4f_coscof_p2);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
y = _mm_mul_ps(y, z);
|
||||||
|
__m128 tmp = _mm_mul_ps(z, *(__m128*)simd4fHalf);
|
||||||
|
y = _mm_sub_ps(y, tmp);
|
||||||
|
y = _mm_add_ps(y, *(__m128*)simd4fOne);
|
||||||
|
|
||||||
|
/* Evaluate the second polynom (Pi/4 <= x <= 0) */
|
||||||
|
|
||||||
|
__m128 y2 = *(__m128*)simd4f_sincof_p0;
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p1);
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_add_ps(y2, *(__m128*)simd4f_sincof_p2);
|
||||||
|
y2 = _mm_mul_ps(y2, z);
|
||||||
|
y2 = _mm_mul_ps(y2, x);
|
||||||
|
y2 = _mm_add_ps(y2, x);
|
||||||
|
|
||||||
|
/* select the correct result from the two polynoms */
|
||||||
|
xmm3 = poly_mask;
|
||||||
|
__m128 ysin2 = _mm_and_ps(xmm3, y2);
|
||||||
|
__m128 ysin1 = _mm_andnot_ps(xmm3, y);
|
||||||
|
y2 = _mm_sub_ps(y2,ysin2);
|
||||||
|
y = _mm_sub_ps(y, ysin1);
|
||||||
|
|
||||||
|
xmm1 = _mm_add_ps(ysin1,ysin2);
|
||||||
|
xmm2 = _mm_add_ps(y,y2);
|
||||||
|
|
||||||
|
/* update the sign */
|
||||||
|
*s = _mm_xor_ps(xmm1, sign_bit_sin);
|
||||||
|
*c = _mm_xor_ps(xmm2, sign_bit_cos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE2_SUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
/* Copyright (C) 2008 José Fonseca
|
||||||
|
http://jrfonseca.blogspot.ca/2008/09/fast-sse2-pow-tables-or-polynomials.html
|
||||||
|
MIT license
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define POLY0(x,c0) _mm_set1_ps(c0)
|
||||||
|
#define POLY1(x,c0,c1) _mm_add_ps(_mm_mul_ps(POLY0(x, c1), x), _mm_set1_ps(c0))
|
||||||
|
#define POLY2(x,c0,c1,c2) _mm_add_ps(_mm_mul_ps(POLY1(x, c1, c2), x), _mm_set1_ps(c0))
|
||||||
|
#define POLY3(x,c0,c1,c2,c3) _mm_add_ps(_mm_mul_ps(POLY2(x, c1, c2, c3), x), _mm_set1_ps(c0))
|
||||||
|
#define POLY4(x,c0,c1,c2,c3,c4) _mm_add_ps(_mm_mul_ps(POLY3(x, c1, c2, c3, c4), x), _mm_set1_ps(c0))
|
||||||
|
#define POLY5(x,c0,c1,c2,c3,c4,c5) _mm_add_ps(_mm_mul_ps(POLY4(x, c1, c2, c3, c4, c5), x), _mm_set1_ps(c0))
|
||||||
|
|
||||||
|
#define EXP_POLY_DEGREE 3
|
||||||
|
#define LOG_POLY_DEGREE 5
|
||||||
|
|
||||||
|
__m128 simd4f_exp2_ps( __m128 x )
|
||||||
|
{
|
||||||
|
__m128i ipart;
|
||||||
|
__m128 fpart, expipart, expfpart;
|
||||||
|
|
||||||
|
x = _mm_min_ps( x, _mm_set1_ps( 129.00000f ) );
|
||||||
|
x = _mm_max_ps( x, _mm_set1_ps( -126.99999f ) );
|
||||||
|
/* ipart = int(x - 0.5) */
|
||||||
|
ipart = _mm_cvtps_epi32( _mm_sub_ps( x, _mm_set1_ps( 0.5f ) ) );
|
||||||
|
/* fpart = x - ipart */
|
||||||
|
fpart = _mm_sub_ps( x, _mm_cvtepi32_ps( ipart ) );
|
||||||
|
/* expipart = (float) (1 << ipart) */
|
||||||
|
expipart = _mm_castsi128_ps( _mm_slli_epi32( _mm_add_epi32( ipart, _mm_set1_epi32( 127 ) ), 23 ) );
|
||||||
|
/* minimax polynomial fit of 2**x, in range [-0.5, 0.5[ */
|
||||||
|
#if EXP_POLY_DEGREE == 5
|
||||||
|
expfpart = POLY5( fpart, 9.9999994e-1f, 6.9315308e-1f, 2.4015361e-1f, 5.5826318e-2f, 8.9893397e-3f, 1.8775767e-3f );
|
||||||
|
#elif EXP_POLY_DEGREE == 4
|
||||||
|
expfpart = POLY4( fpart, 1.0000026f, 6.9300383e-1f, 2.4144275e-1f, 5.2011464e-2f, 1.3534167e-2f );
|
||||||
|
#elif EXP_POLY_DEGREE == 3
|
||||||
|
expfpart = POLY3( fpart, 9.9992520e-1f, 6.9583356e-1f, 2.2606716e-1f, 7.8024521e-2f );
|
||||||
|
#elif EXP_POLY_DEGREE == 2
|
||||||
|
expfpart = POLY2( fpart, 1.0017247f, 6.5763628e-1f, 3.3718944e-1f );
|
||||||
|
#else
|
||||||
|
#error
|
||||||
|
#endif
|
||||||
|
return _mm_mul_ps(expipart, expfpart);
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 simd4f_log2_ps( __m128 x )
|
||||||
|
{
|
||||||
|
__m128i expmask, mantmask, i;
|
||||||
|
__m128 one, vexp, mant, logmant;
|
||||||
|
|
||||||
|
expmask = _mm_set1_epi32( 0x7f800000 );
|
||||||
|
mantmask = _mm_set1_epi32( 0x007fffff );
|
||||||
|
one = _mm_set1_ps( 1.0f );
|
||||||
|
i = _mm_castps_si128( x );
|
||||||
|
/* exp = (float) exponent(x) */
|
||||||
|
vexp = _mm_cvtepi32_ps( _mm_sub_epi32( _mm_srli_epi32( _mm_and_si128( i, expmask ), 23 ), _mm_set1_epi32( 127 ) ) );
|
||||||
|
/* mant = (float) mantissa(x) */
|
||||||
|
mant = _mm_or_ps( _mm_castsi128_ps( _mm_and_si128( i, mantmask ) ), one );
|
||||||
|
/* Minimax polynomial fit of log2(x)/(x - 1), for x in range [1, 2[
|
||||||
|
* These coefficients can be generate with
|
||||||
|
* http://www.boost.org/doc/libs/1_36_0/libs/math/doc/sf_and_dist/html/math_toolkit/toolkit/internals2/minimax.html
|
||||||
|
*/
|
||||||
|
#if LOG_POLY_DEGREE == 6
|
||||||
|
logmant = POLY5( mant, 3.11578814719469302614f, -3.32419399085241980044f, 2.59883907202499966007f, -1.23152682416275988241f, 0.318212422185251071475f, -0.0344359067839062357313f );
|
||||||
|
#elif LOG_POLY_DEGREE == 5
|
||||||
|
logmant = POLY4( mant, 2.8882704548164776201f, -2.52074962577807006663f, 1.48116647521213171641f, -0.465725644288844778798f, 0.0596515482674574969533f );
|
||||||
|
#elif LOG_POLY_DEGREE == 4
|
||||||
|
logmant = POLY3( mant, 2.61761038894603480148f, -1.75647175389045657003f, 0.688243882994381274313f, -0.107254423828329604454f );
|
||||||
|
#elif LOG_POLY_DEGREE == 3
|
||||||
|
logmant = POLY2( mant, 2.28330284476918490682f, -1.04913055217340124191f, 0.204446009836232697516f );
|
||||||
|
#else
|
||||||
|
#error
|
||||||
|
#endif
|
||||||
|
/* This effectively increases the polynomial degree by one, but ensures that log2(1) == 0*/
|
||||||
|
logmant = _mm_mul_ps( logmant, _mm_sub_ps(mant, one ) );
|
||||||
|
return _mm_add_ps( logmant, vexp );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__m128 simd4f_pow_ps( __m128 x, __m128 y )
|
||||||
|
{
|
||||||
|
return simd4f_exp2_ps( _mm_mul_ps( simd4f_log2_ps( x ), y ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE2_SUPPORT
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
By Potatoswatter
|
||||||
|
http://stackoverflow.com/questions/6475373/optimizations-for-pow-with-const-non-integer-exponent
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CC_ALWAYSINLINE
|
||||||
|
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
|
||||||
|
#define CC_ALWAYSINLINE __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
#define CC_ALWAYSINLINE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline CC_ALWAYSINLINE __m128 simd4f_fastpow_ps( __m128 arg, uint32_t expnum, uint32_t expden, uint32_t coeffnum, uint32_t coeffden )
|
||||||
|
{
|
||||||
|
__m128 ret = arg;
|
||||||
|
float corrfactor, powfactor;
|
||||||
|
/* Apply a constant pre-correction factor. */
|
||||||
|
corrfactor = exp2( 127.0 * expden / expnum - 127.0 ) * pow( 1.0 * coeffnum / coeffden, 1.0 * expden / expnum );
|
||||||
|
powfactor = 1.0 * expnum / expden;
|
||||||
|
ret = _mm_mul_ps( ret, _mm_set1_ps( corrfactor ) );
|
||||||
|
/* Reinterpret arg as integer to obtain logarithm. */
|
||||||
|
ret = _mm_cvtepi32_ps( _mm_castps_si128( ret ) );
|
||||||
|
/* Multiply logarithm by power. */
|
||||||
|
ret = _mm_mul_ps( ret, _mm_set1_ps( powfactor ) );
|
||||||
|
/* Convert back to "integer" to exponentiate. */
|
||||||
|
ret = _mm_castsi128_ps( _mm_cvtps_epi32( ret ) );
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 simd4f_pow12d5_ps( __m128 arg )
|
||||||
|
{
|
||||||
|
/* Lower exponents provide lower initial error, but too low causes overflow. */
|
||||||
|
__m128 xf = simd4f_fastpow_ps( arg, 4, 5, (int)( 1.38316186f * (float)1e9 ), (int)1e9 );
|
||||||
|
/* Imprecise 4-cycle sqrt is still far better than fastpow, good enough. */
|
||||||
|
__m128 xfm4 = _mm_rsqrt_ps( xf );
|
||||||
|
__m128 xf4 = _mm_mul_ps( xf, xfm4 );
|
||||||
|
/* Precisely calculate x^2 and x^3 */
|
||||||
|
__m128 x2 = _mm_mul_ps( arg, arg );
|
||||||
|
__m128 x3 = _mm_mul_ps( x2, arg );
|
||||||
|
/* Overestimate of x^2 * x^0.4 */
|
||||||
|
x2 = _mm_mul_ps( x2, xf4 );
|
||||||
|
/* Get x^-0.2 from x^0.4, and square it for x^-0.4. Combine into x^-0.6. */
|
||||||
|
__m128 xfm2 = _mm_rsqrt_ps( xf4 );
|
||||||
|
x3 = _mm_mul_ps( x3, xfm4 );
|
||||||
|
x3 = _mm_mul_ps( x3, xfm2 );
|
||||||
|
return _mm_mul_ps( _mm_add_ps( x2, x3 ), _mm_set1_ps( 1.0f/1.960131704207789f * 0.9999f ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
__m128 simd4f_pow5d12_ps( __m128 arg )
|
||||||
|
{
|
||||||
|
/* 5/12 is too small, so compute the 4th root of 20/12 instead. */
|
||||||
|
/* 20/12 = 5/3 = 1 + 2/3 = 2 - 1/3. 2/3 is a suitable argument for fastpow. */
|
||||||
|
/* weighting coefficient: a^-1/2 = 2 a; a = 2^-2/3 */
|
||||||
|
__m128 xf = simd4f_fastpow_ps( arg, 2, 3, (int)( 0.629960524947437f * (float)1e9 ), (int)1e9 );
|
||||||
|
__m128 xover = _mm_mul_ps( arg, xf );
|
||||||
|
__m128 xfm1 = _mm_rsqrt_ps( xf );
|
||||||
|
__m128 x2 = _mm_mul_ps( arg, arg );
|
||||||
|
__m128 xunder = _mm_mul_ps( x2, xfm1 );
|
||||||
|
/* sqrt2 * over + 2 * sqrt2 * under */
|
||||||
|
__m128 xavg = _mm_mul_ps( _mm_set1_ps( 1.0f/( 3.0f * 0.629960524947437f ) * 0.999852f ), _mm_add_ps( xover, xunder ) );
|
||||||
|
xavg = _mm_mul_ps( xavg, _mm_rsqrt_ps( xavg ) );
|
||||||
|
xavg = _mm_mul_ps( xavg, _mm_rsqrt_ps( xavg ) );
|
||||||
|
return xavg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
410
lib/graphics_utils/mipmap/cpusimd.h
Normal file
410
lib/graphics_utils/mipmap/cpusimd.h
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008-2016 Alexis Naveros.
|
||||||
|
*
|
||||||
|
* The SIMD trigonometry functions are Copyright (C) 2007 Julien Pommier
|
||||||
|
* See copyright notice for simd4f_sin_ps(), simd4f_cos_ps(), simd4f_sincos_ps()
|
||||||
|
*
|
||||||
|
* Portions developed under contract to the SURVICE Engineering Company.
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CPUSIMD_H
|
||||||
|
#define CPUSIMD_H
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if __MMX__ || CPU_ENABLE_MMX
|
||||||
|
#include <mmintrin.h>
|
||||||
|
#define CPU_MMX_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __SSE__ || _M_X64 || _M_IX86_FP >= 1 || CPU_ENABLE_SSE
|
||||||
|
#include <xmmintrin.h>
|
||||||
|
#define CPU_SSE_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __SSE2__ || _M_X64 || _M_IX86_FP >= 2 || CPU_ENABLE_SSE2
|
||||||
|
#include <emmintrin.h>
|
||||||
|
#define CPU_SSE2_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __SSE3__ || __AVX__ || CPU_ENABLE_SSE3
|
||||||
|
#include <pmmintrin.h>
|
||||||
|
#define CPU_SSE3_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __SSSE3__ || __AVX__ || CPU_ENABLE_SSSE3
|
||||||
|
#include <tmmintrin.h>
|
||||||
|
#define CPU_SSSE3_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __SSE4_1__ || __AVX__ || CPU_ENABLE_SSE4_1
|
||||||
|
#include <smmintrin.h>
|
||||||
|
#define CPU_SSE4_1_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __SSE4_2__ || CPU_ENABLE_SSE4_2
|
||||||
|
#include <nmmintrin.h>
|
||||||
|
#define CPU_SSE4_2_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __SSE4A__ || CPU_ENABLE_SSE4A
|
||||||
|
#include <ammintrin.h>
|
||||||
|
#define CPU_SSE4A_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __AVX__ || CPU_ENABLE_AVX
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define CPU_AVX_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __AVX2__ || CPU_ENABLE_AVX2
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define CPU_AVX2_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __XOP__ || CPU_ENABLE_XOP
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define CPU_XOP_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __FMA3__ || CPU_ENABLE_FMA3
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define CPU_FMA3_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __FMA4__ || CPU_ENABLE_FMA4
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define CPU_FMA4_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __RDRND__ || CPU_ENABLE_RDRND
|
||||||
|
#include <immintrin.h>
|
||||||
|
#define CPU_RDRND_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __POPCNT__ || CPU_ENABLE_POPCNT
|
||||||
|
#include <popcntintrin.h>
|
||||||
|
#define CPU_POPCNT_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __LZCNT__ || CPU_ENABLE_LZCNT
|
||||||
|
#include <lzcntintrin.h>
|
||||||
|
#define CPU_LZCNT_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __F16C__ || CPU_ENABLE_F16C
|
||||||
|
#include <f16cintrin.h>
|
||||||
|
#define CPU_F16C_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __BMI__ || CPU_ENABLE_BMI
|
||||||
|
#include <bmiintrin.h>
|
||||||
|
#define CPU_BMI_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __BMI2__ || CPU_ENABLE_BMI2
|
||||||
|
#include <bmi2intrin.h>
|
||||||
|
#define CPU_BMI2_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
#if __TBM__ || CPU_ENABLE_TBM
|
||||||
|
#include <tbmintrin.h>
|
||||||
|
#define CPU_TBM_SUPPORT (1)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
|
||||||
|
#define CPU_ALIGN16 __attribute__((aligned(16)))
|
||||||
|
#define CPU_ALIGN32 __attribute__((aligned(32)))
|
||||||
|
#define CPU_ALIGN64 __attribute__((aligned(64)))
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define CPU_ALIGN16 __declspec(align(16))
|
||||||
|
#define CPU_ALIGN64 __declspec(align(64))
|
||||||
|
#else
|
||||||
|
#define CPU_ALIGN16
|
||||||
|
#define CPU_ALIGN32
|
||||||
|
#define CPU_ALIGN64
|
||||||
|
#warning "SSE/AVX Disabled: Unsupported Compiler."
|
||||||
|
#undef CPU_SSE_SUPPORT
|
||||||
|
#undef CPU_SSE2_SUPPORT
|
||||||
|
#undef CPU_SSE3_SUPPORT
|
||||||
|
#undef CPU_SSSE3_SUPPORT
|
||||||
|
#undef CPU_SSE4_1_SUPPORT
|
||||||
|
#undef CPU_SSE4_2_SUPPORT
|
||||||
|
#undef CPU_AVX_SUPPORT
|
||||||
|
#undef CPU_AVX2_SUPPORT
|
||||||
|
#undef CPU_XOP_SUPPORT
|
||||||
|
#undef CPU_FMA3_SUPPORT
|
||||||
|
#undef CPU_FMA4_SUPPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE_SUPPORT
|
||||||
|
#define CPU_APPROX_DIV_FLOAT(z,w) _mm_cvtss_f32(_mm_mul_ss(_mm_set_ss(z),_mm_rcp_ss(_mm_set_ss(w))))
|
||||||
|
#define CPU_APPROX_SQRT_FLOAT(z) _mm_cvtss_f32(_mm_mul_ss(_mm_set_ss(z),_mm_rsqrt_ss(_mm_set_ss(z))))
|
||||||
|
#define CPU_APPROX_RSQRT_FLOAT(z) _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(z)))
|
||||||
|
#define CPU_APPROX_DIVSQRT_FLOAT(z,w) _mm_cvtss_f32(_mm_mul_ss(_mm_set_ss(z),_mm_rsqrt_ss(_mm_set_ss(w))))
|
||||||
|
#else
|
||||||
|
#define CPU_APPROX_DIV_FLOAT(z,w) ((z)/(w))
|
||||||
|
#define CPU_APPROX_SQRT_FLOAT(z) (sqrtf(z))
|
||||||
|
#define CPU_APPROX_RSQRT_FLOAT(z) (1.0/sqrtf(z))
|
||||||
|
#define CPU_APPROX_DIVSQRT_FLOAT(z,w) ((z)/sqrtf(w))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE3_SUPPORT
|
||||||
|
#define CPU_HADD_PS(vx,vy) _mm_hadd_ps(vx,vy)
|
||||||
|
#define CPU_HADD_PD(vx,vy) _mm_hadd_pd(vx,vy)
|
||||||
|
#elif CPU_SSE_SUPPORT
|
||||||
|
static inline __m128 CPU_HADD_PS( __m128 vx, __m128 vy )
|
||||||
|
{
|
||||||
|
__m128 vh, vl;
|
||||||
|
vh = _mm_shuffle_ps( vx, vy, _MM_SHUFFLE(3,1,3,1) );
|
||||||
|
vl = _mm_shuffle_ps( vx, vy, _MM_SHUFFLE(2,0,2,0) );
|
||||||
|
return _mm_add_ps( vh, vl );
|
||||||
|
}
|
||||||
|
#define CPU_HADD_PD(vx,vy) _mm_add_sd(vx,_mm_unpackhi_pd(vy,vy))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE4_1_SUPPORT
|
||||||
|
#define CPU_CVT_U8_TO_I32(x,vzero) _mm_cvtepu8_epi32(x)
|
||||||
|
#define CPU_CVT_S8_TO_I32(x,vzero) _mm_cvtepi8_epi32(x)
|
||||||
|
#elif CPU_SSE2_SUPPORT
|
||||||
|
#define CPU_CVT_U8_TO_I32(x,vzero) _mm_unpacklo_epi16(_mm_unpacklo_epi8((x),(vzero)),(vzero))
|
||||||
|
static inline __m128i CPU_CVT_S8_TO_I32( __m128i vx, __m128i vzero )
|
||||||
|
{
|
||||||
|
__m128i vsign;
|
||||||
|
vsign = _mm_cmpgt_epi8( vzero, vx );
|
||||||
|
return _mm_unpacklo_epi16( _mm_unpacklo_epi8( vx, vsign ), _mm_unpacklo_epi8( vsign, vsign ) );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE4_1_SUPPORT
|
||||||
|
#define CPU_BLENDV_PS(x,y,mask) _mm_blendv_ps(x,y,mask)
|
||||||
|
#define CPU_BLENDV_PD(x,y,mask) _mm_blendv_pd(x,y,mask)
|
||||||
|
#elif CPU_SSE2_SUPPORT
|
||||||
|
#define CPU_BLENDV_PS(x,y,mask) _mm_or_ps(_mm_andnot_ps(mask,x),_mm_and_ps(y,mask))
|
||||||
|
#define CPU_BLENDV_PD(x,y,mask) _mm_or_pd(_mm_andnot_pd(mask,x),_mm_and_pd(y,mask))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
CPU_FMADD = ((f0*f1)+t0)
|
||||||
|
CPU_FMSUB = ((f0*f1)-t0)
|
||||||
|
*/
|
||||||
|
#if CPU_FMA3_SUPPORT
|
||||||
|
#define CPU_FMADD_SS(f0,f1,t0) _mm_fmadd_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMADD_PS(f0,f1,t0) _mm_fmadd_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMADD_SD(f0,f1,t0) _mm_fmadd_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMADD_PD(f0,f1,t0) _mm_fmadd_pd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_SS(f0,f1,t0) _mm_fmsub_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_PS(f0,f1,t0) _mm_fmsub_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_SD(f0,f1,t0) _mm_fmsub_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_PD(f0,f1,t0) _mm_fmsub_pd(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_SS(f0,f1,t0) _mm256_fmadd_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_PS(f0,f1,t0) _mm256_fmadd_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_SD(f0,f1,t0) _mm256_fmadd_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_PD(f0,f1,t0) _mm256_fmadd_pd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_SS(f0,f1,t0) _mm256_fmsub_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_PS(f0,f1,t0) _mm256_fmsub_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_SD(f0,f1,t0) _mm256_fmsub_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_PD(f0,f1,t0) _mm256_fmsub_pd(f0,f1,t0)
|
||||||
|
#elif CPU_FMA4_SUPPORT
|
||||||
|
#define CPU_FMADD_SS(f0,f1,t0) _mm_macc_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMADD_PS(f0,f1,t0) _mm_macc_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMADD_SD(f0,f1,t0) _mm_macc_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMADD_PD(f0,f1,t0) _mm_macc_pd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_SS(f0,f1,t0) _mm_msub_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_PS(f0,f1,t0) _mm_msub_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_SD(f0,f1,t0) _mm_msub_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB_PD(f0,f1,t0) _mm_msub_pd(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_SS(f0,f1,t0) _mm256_macc_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_PS(f0,f1,t0) _mm256_macc_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_SD(f0,f1,t0) _mm256_macc_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMADD256_PD(f0,f1,t0) _mm256_macc_pd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_SS(f0,f1,t0) _mm256_msub_ss(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_PS(f0,f1,t0) _mm256_msub_ps(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_SD(f0,f1,t0) _mm256_msub_sd(f0,f1,t0)
|
||||||
|
#define CPU_FMSUB256_PD(f0,f1,t0) _mm256_msub_pd(f0,f1,t0)
|
||||||
|
#else
|
||||||
|
#define CPU_FMADD_SS(f0,f1,t0) _mm_add_ss(_mm_mul_ss(f0,f1),t0)
|
||||||
|
#define CPU_FMADD_PS(f0,f1,t0) _mm_add_ps(_mm_mul_ps(f0,f1),t0)
|
||||||
|
#define CPU_FMADD_SD(f0,f1,t0) _mm_add_sd(_mm_mul_sd(f0,f1),t0)
|
||||||
|
#define CPU_FMADD_PD(f0,f1,t0) _mm_add_pd(_mm_mul_pd(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB_SS(f0,f1,t0) _mm_sub_ss(_mm_mul_ss(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB_PS(f0,f1,t0) _mm_sub_ps(_mm_mul_ps(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB_SD(f0,f1,t0) _mm_sub_sd(_mm_mul_sd(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB_PD(f0,f1,t0) _mm_sub_pd(_mm_mul_pd(f0,f1),t0)
|
||||||
|
#define CPU_FMADD256_SS(f0,f1,t0) _mm256_add_ss(_mm256_mul_ss(f0,f1),t0)
|
||||||
|
#define CPU_FMADD256_PS(f0,f1,t0) _mm256_add_ps(_mm256_mul_ps(f0,f1),t0)
|
||||||
|
#define CPU_FMADD256_SD(f0,f1,t0) _mm256_add_sd(_mm256_mul_sd(f0,f1),t0)
|
||||||
|
#define CPU_FMADD256_PD(f0,f1,t0) _mm256_add_pd(_mm256_mul_pd(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB256_SS(f0,f1,t0) _mm256_sub_ss(_mm256_mul_ss(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB256_PS(f0,f1,t0) _mm256_sub_ps(_mm256_mul_ps(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB256_SD(f0,f1,t0) _mm256_sub_sd(_mm256_mul_sd(f0,f1),t0)
|
||||||
|
#define CPU_FMSUB256_PD(f0,f1,t0) _mm256_sub_pd(_mm256_mul_pd(f0,f1),t0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE_SUPPORT
|
||||||
|
|
||||||
|
extern const uint32_t simd4fSignMask[4];
|
||||||
|
extern const uint32_t simd4fSignMaskInv[4];
|
||||||
|
extern const float simd4fHalf[4];
|
||||||
|
extern const float simd4fOne[4];
|
||||||
|
extern const float simd4fTwo[4];
|
||||||
|
extern const float simd4fThree[4];
|
||||||
|
extern const uint32_t simd4uOne[4];
|
||||||
|
extern const uint32_t simd4uOneInv[4];
|
||||||
|
extern const uint32_t simd4uTwo[4];
|
||||||
|
extern const uint32_t simd4uFour[4];
|
||||||
|
extern const float simd4fQuarter[4];
|
||||||
|
extern const float simd4fPi[4];
|
||||||
|
extern const float simd4fZeroOneTwoThree[4];
|
||||||
|
extern const uint32_t simd4fAlphaMask[4];
|
||||||
|
extern const float simd4f255[4];
|
||||||
|
extern const float simd4f255Inv[4];
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE2_SUPPORT
|
||||||
|
|
||||||
|
/* Input range between -8192 and 8192 */
|
||||||
|
__m128 simd4f_sin_ps( __m128 x );
|
||||||
|
__m128 simd4f_cos_ps( __m128 x );
|
||||||
|
void simd4f_sincos_ps( __m128 x, __m128 *s, __m128 *c );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CPU_SSE2_SUPPORT
|
||||||
|
|
||||||
|
__m128 simd4f_exp2_ps( __m128 x );
|
||||||
|
__m128 simd4f_log2_ps( __m128 x );
|
||||||
|
__m128 simd4f_pow_ps( __m128 x, __m128 y );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CPU_SSE2_SUPPORT
|
||||||
|
|
||||||
|
__m128 simd4f_pow12d5_ps( __m128 arg );
|
||||||
|
__m128 simd4f_pow5d12_ps( __m128 arg );
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE2_SUPPORT
|
||||||
|
|
||||||
|
#ifndef CC_ALWAYSINLINE
|
||||||
|
#if defined(__GNUC__) || defined(__INTEL_COMPILER)
|
||||||
|
#define CC_ALWAYSINLINE __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
#define CC_ALWAYSINLINE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline CC_ALWAYSINLINE __m128 simd4f_pow12d5_inline_ps( __m128 vx )
|
||||||
|
{
|
||||||
|
__m128 vpow, vpwsqrtinv, vpwsqrt, vx2;
|
||||||
|
vx2 = _mm_mul_ps( vx, vx );
|
||||||
|
vpow = _mm_castsi128_ps( _mm_cvtps_epi32( _mm_mul_ps( _mm_cvtepi32_ps( _mm_castps_si128( _mm_mul_ps( vx, _mm_set1_ps( 5417434112.0f ) ) ) ), _mm_set1_ps( 0.8f ) ) ) );
|
||||||
|
vpwsqrtinv = _mm_rsqrt_ps( vpow );
|
||||||
|
vpwsqrt = _mm_mul_ps( vpow, vpwsqrtinv );
|
||||||
|
return _mm_mul_ps( _mm_add_ps( _mm_mul_ps( vx2, vpwsqrt ), _mm_mul_ps( _mm_mul_ps( _mm_mul_ps( vx2, vx ), vpwsqrtinv ), _mm_rsqrt_ps( vpwsqrt ) ) ), _mm_set1_ps( 0.51011878327f ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline CC_ALWAYSINLINE __m128 simd4f_pow5d12_inline_ps( __m128 vx )
|
||||||
|
{
|
||||||
|
__m128 vpow;
|
||||||
|
vpow = _mm_castsi128_ps( _mm_cvtps_epi32( _mm_mul_ps( _mm_cvtepi32_ps( _mm_castps_si128( _mm_mul_ps( vx, _mm_set1_ps( 6521909350804488192.0f ) ) ) ), _mm_set1_ps( 0.666666666666f ) ) ) );
|
||||||
|
vx = _mm_mul_ps( _mm_add_ps( _mm_mul_ps( vx, vpow ), _mm_mul_ps( _mm_mul_ps( vx, vx ), _mm_rsqrt_ps( vpow ) ) ), _mm_set1_ps( 0.5290553722f ) );
|
||||||
|
#if 0
|
||||||
|
vx = _mm_mul_ps( vx, _mm_rsqrt_ps( vx ) );
|
||||||
|
vx = _mm_mul_ps( vx, _mm_rsqrt_ps( vx ) );
|
||||||
|
#else
|
||||||
|
vx = _mm_sqrt_ps( vx );
|
||||||
|
vx = _mm_sqrt_ps( vx );
|
||||||
|
#endif
|
||||||
|
return vx;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#if CPU_SSE_SUPPORT
|
||||||
|
|
||||||
|
static inline void simdPrintDebugSSE4f( char *str, __m128 v )
|
||||||
|
{
|
||||||
|
float CPU_ALIGN16 store[4];
|
||||||
|
_mm_store_ps( (void *)store, v );
|
||||||
|
printf( "%s %f %f %f %f\n", str, (double)store[0], (double)store[1], (double)store[2], (double)store[3] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void simdPrintDebugSSE2d( char *str, __m128d v )
|
||||||
|
{
|
||||||
|
double CPU_ALIGN16 store[2];
|
||||||
|
_mm_store_pd( (void *)store, v );
|
||||||
|
printf( "%s %f %f\n", str, store[0], store[1] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void simdPrintDebugSSE16u8( char *str, __m128i v )
|
||||||
|
{
|
||||||
|
uint8_t CPU_ALIGN16 store[16];
|
||||||
|
_mm_store_si128( (void *)store, v );
|
||||||
|
printf( "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", str, store[0], store[1], store[2], store[3], store[4], store[5], store[6], store[7], store[8], store[9], store[10], store[11], store[12], store[13], store[14], store[15] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void simdPrintDebugSSE8u16( char *str, __m128i v )
|
||||||
|
{
|
||||||
|
uint16_t CPU_ALIGN16 store[8];
|
||||||
|
_mm_store_si128( (void *)store, v );
|
||||||
|
printf( "%s %d %d %d %d %d %d %d %d\n", str, store[0], store[1], store[2], store[3], store[4], store[5], store[6], store[7] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void simdPrintDebugSSE4u32( char *str, __m128i v )
|
||||||
|
{
|
||||||
|
uint32_t CPU_ALIGN16 store[4];
|
||||||
|
_mm_store_si128( (void *)store, v );
|
||||||
|
printf( "%s %d %d %d %d\n", str, store[0], store[1], store[2], store[3] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void simdPrintDebugSSE2u64( char *str, __m128i v )
|
||||||
|
{
|
||||||
|
uint64_t CPU_ALIGN16 store[2];
|
||||||
|
_mm_store_si128( (void *)store, v );
|
||||||
|
printf( "%s %lld %lld\n", str, (long long)store[0], (long long)store[1] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
77
lib/graphics_utils/mipmap/img.h
Normal file
77
lib/graphics_utils/mipmap/img.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Copyright (c) 2007-2017 Alexis Naveros.
|
||||||
|
* Portions developed under contract to the SURVICE Engineering Company.
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef IMG_H
|
||||||
|
#define IMG_H
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int type;
|
||||||
|
int bytesperpixel;
|
||||||
|
int bytesperline;
|
||||||
|
} imgFormat;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
IMG_FORMAT_TYPE_ANY,
|
||||||
|
IMG_FORMAT_TYPE_RGB24,
|
||||||
|
IMG_FORMAT_TYPE_BGR24,
|
||||||
|
IMG_FORMAT_TYPE_RGBX32,
|
||||||
|
IMG_FORMAT_TYPE_BGRX32,
|
||||||
|
IMG_FORMAT_TYPE_RGBA32,
|
||||||
|
IMG_FORMAT_TYPE_BGRA32,
|
||||||
|
IMG_FORMAT_TYPE_GRAYSCALE,
|
||||||
|
IMG_FORMAT_TYPE_GRAYALPHA
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
imgFormat format;
|
||||||
|
void *data;
|
||||||
|
} imgImage;
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
void imgCopyRect( imgImage *image, int dstx, int dsty, int srcx, int srcy, int sizex, int sizey );
|
||||||
|
|
||||||
|
void (*imgBlendGetFunction( imgImage *dstimage, imgImage *srcimage ))( imgImage *dstimage, int dstx, int dsty, imgImage *srcimage );
|
||||||
|
int imgBlendImage( imgImage *dstimage, int dstx, int dsty, imgImage *srcimage );
|
||||||
|
|
||||||
|
void imgAllocCopy( imgImage *dst, imgImage *src );
|
||||||
|
void imgAllocCopyExtendBorder( imgImage *dstimage, imgImage *srcimage, int extendsize );
|
||||||
|
void imgAllocExtractChannel( imgImage *dst, imgImage *src, int channelindex );
|
||||||
|
void imgAllocExtractChannelExtendBorder( imgImage *dstimage, imgImage *srcimage, int channelindex, int extendsize );
|
||||||
|
void imgAllocCopyChannelToAlpha( imgImage *dstimage, imgImage *srcimage, int channelindex, unsigned char r, unsigned char g, unsigned char b );
|
||||||
|
void imgAllocAdjustBrightnessContrast( imgImage *dstimage, imgImage *srcimage, float brightness, float contrast );
|
||||||
|
|
||||||
|
void imgFree( imgImage *image );
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
4098
lib/graphics_utils/mipmap/imgresize.c
Normal file
4098
lib/graphics_utils/mipmap/imgresize.c
Normal file
File diff suppressed because it is too large
Load Diff
150
lib/graphics_utils/mipmap/imgresize.h
Normal file
150
lib/graphics_utils/mipmap/imgresize.h
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
/* -----------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014-2017 Alexis Naveros.
|
||||||
|
* Portions developed under contract to the SURVICE Engineering Company.
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
*
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
*
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef IMGRESIZE_H
|
||||||
|
#define IMGRESIZE_H
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* Specify filter type, from the IM_REDUCE_FILTER_* list */
|
||||||
|
int filter;
|
||||||
|
/* High quality, a little slow: hopcount=3; */
|
||||||
|
/* Good quality, much faster: hopcount=2; */
|
||||||
|
int hopcount;
|
||||||
|
/* Strong preservation/amplification of details: alpha=2.0f; */
|
||||||
|
/* Mild preservation/amplification of details: alpha=6.0f; */
|
||||||
|
float alpha;
|
||||||
|
/* NORMALMAP filters: factor to amyplify normals on X and Y before normalization */
|
||||||
|
float amplifynormal;
|
||||||
|
/* NORMALMAP_SUSTAIN filters: Preserve a factor of deviation "energy" as calculated by sqrtf(x*x+y*y) */
|
||||||
|
float normalsustainfactor;
|
||||||
|
} imReduceOptions;
|
||||||
|
|
||||||
|
static inline void imReduceSetOptions( imReduceOptions *options, int filter, int hopcount, float alpha, float amplifynormal, float normalsustainfactor )
|
||||||
|
{
|
||||||
|
options->filter = filter;
|
||||||
|
options->hopcount = hopcount;
|
||||||
|
options->alpha = alpha;
|
||||||
|
options->amplifynormal = amplifynormal;
|
||||||
|
options->normalsustainfactor = normalsustainfactor;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Reduce the image's dimensions by an integer divisor ~ this is fairly fast */
|
||||||
|
int imReduceImageKaiserDataDivisor( unsigned char *dstdata, unsigned char *srcdata, int width, int height, int bytesperpixel, int bytesperline, int sizedivisor, imReduceOptions *options );
|
||||||
|
/* Same as imReduceImageKaiserDataDivisor(), but imgdst is allocated */
|
||||||
|
int imReduceImageKaiserDivisor( imgImage *imgdst, imgImage *imgsrc, int sizedivisor, imReduceOptions *options );
|
||||||
|
|
||||||
|
|
||||||
|
/* Reduce the image's dimensions to match the newwidth and newheight ~ this is a little slower */
|
||||||
|
int imReduceImageKaiserData( unsigned char *dstdata, unsigned char *srcdata, int width, int height, int bytesperpixel, int bytesperline, int newwidth, int newheight, imReduceOptions *options );
|
||||||
|
/* Same as imReduceImageKaiserData(), but imgdst is allocated */
|
||||||
|
int imReduceImageKaiser( imgImage *imgdst, imgImage *imgsrc, int newwidth, int newheight, imReduceOptions *options );
|
||||||
|
|
||||||
|
|
||||||
|
/* Resize by half with a dumb box filter ~ don't use that except for the smallest mipmaps */
|
||||||
|
/* Filters with ALPHANORM and/or SUSTAIN keywords are processed as the regular base filter only */
|
||||||
|
int imReduceImageHalfBoxData( unsigned char *dstdata, unsigned char *srcdata, int width, int height, int bytesperpixel, int bytesperline, imReduceOptions *options );
|
||||||
|
int imReduceImageHalfBox( imgImage *imgdst, imgImage *imgsrc, imReduceOptions *options );
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Keywords for image reduction filters
|
||||||
|
|
||||||
|
LINEAR: Data is linear, note that this is *not* the format of typical diffuse textures
|
||||||
|
SRGB: Color is in sRGB space, any alpha is presumed linear
|
||||||
|
NORMALMAP: RGB represents a XYZ vector as (2.0*RGB)-1.0f, any alpha is presumed linear
|
||||||
|
|
||||||
|
ALPHANORM: Alpha normalization, the weight of pixels is proportional to their alpha values
|
||||||
|
(do you have "black" fully transparent pixels? please use an ALPHANORM filter)
|
||||||
|
SUSTAIN: The "energy" of the normal map is sustained, amplified to preserve the level of details
|
||||||
|
Note that this filter is rather slow (set options->normalsustainfactor to 0.75 or so)
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
/* Linear space */
|
||||||
|
IM_REDUCE_FILTER_LINEAR,
|
||||||
|
IM_REDUCE_FILTER_LINEAR_ALPHANORM,
|
||||||
|
|
||||||
|
/* sRGB space (probably what you want for diffuse textures) */
|
||||||
|
IM_REDUCE_FILTER_SRGB,
|
||||||
|
IM_REDUCE_FILTER_SRGB_ALPHANORM,
|
||||||
|
|
||||||
|
/* RGB represents a XYZ vector as (2.0*RGB)-1.0f, any alpha is presumed linear */
|
||||||
|
IM_REDUCE_FILTER_NORMALMAP,
|
||||||
|
IM_REDUCE_FILTER_NORMALMAP_ALPHANORM,
|
||||||
|
IM_REDUCE_FILTER_NORMALMAP_SUSTAIN,
|
||||||
|
IM_REDUCE_FILTER_NORMALMAP_SUSTAIN_ALPHANORM,
|
||||||
|
|
||||||
|
/* Custom specialized filters */
|
||||||
|
IM_REDUCE_FILTER_WATERMAP,
|
||||||
|
IM_REDUCE_FILTER_PLANTMAP,
|
||||||
|
IM_REDUCE_FILTER_FOLLIAGE,
|
||||||
|
IM_REDUCE_FILTER_SKY,
|
||||||
|
IM_REDUCE_FILTER_FOG
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#define IM_MIPMAP_CASCADE_MAX (16)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int layercount;
|
||||||
|
int bytesperpixel;
|
||||||
|
int bytesperline;
|
||||||
|
imReduceOptions *options;
|
||||||
|
void *mipmap[IM_MIPMAP_CASCADE_MAX];
|
||||||
|
} imMipmapCascade;
|
||||||
|
|
||||||
|
|
||||||
|
int imBuildMipmapCascade( imMipmapCascade *cascade, void *imagedata, int width, int height, int layercount, int bytesperpixel, int bytesperline, imReduceOptions *options, int cascadeflags );
|
||||||
|
|
||||||
|
void imFreeMipmapCascade( imMipmapCascade *cascade );
|
||||||
|
|
||||||
|
/* For base texture, propagate RGB channels to neighbors if they are fully transparent (ignored if bytesperpixel != 4 ) */
|
||||||
|
#define IM_CASCADE_FLAGS_COLOR_BORDER_BASE (0x1)
|
||||||
|
/* For generated mipmaps, propagate RGB channels to neighbors if they are fully transparent (ignored if bytesperpixel != 4 ) */
|
||||||
|
#define IM_CASCADE_FLAGS_COLOR_BORDER_MIPMAPS (0x2)
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
void imPropagateAlphaBorder( unsigned char *imagedata, int width, int height, int bytesperpixel, int bytesperline );
|
||||||
|
|
||||||
|
|
||||||
|
////
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -98,6 +98,8 @@ public:
|
|||||||
//! fills the surface with given color
|
//! fills the surface with given color
|
||||||
virtual void fill(const SColor &color) =0;
|
virtual void fill(const SColor &color) =0;
|
||||||
|
|
||||||
|
virtual void setDeleteMemory(bool val) = 0;
|
||||||
|
|
||||||
//! get the amount of Bits per Pixel of the given color format
|
//! get the amount of Bits per Pixel of the given color format
|
||||||
static u32 getBitsPerPixelFromFormat(const ECOLOR_FORMAT format)
|
static u32 getBitsPerPixelFromFormat(const ECOLOR_FORMAT format)
|
||||||
{
|
{
|
||||||
|
@ -42,7 +42,9 @@ public:
|
|||||||
//! Creates a surface from the file
|
//! Creates a surface from the file
|
||||||
/** \param file File handle to check.
|
/** \param file File handle to check.
|
||||||
\return Pointer to newly created image, or 0 upon error. */
|
\return Pointer to newly created image, or 0 upon error. */
|
||||||
virtual IImage* loadImage(io::IReadFile* file) const = 0;
|
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const = 0;
|
||||||
|
virtual core::dimension2du getImageSize(io::IReadFile* file) const { return core::dimension2du(0, 0); }
|
||||||
|
virtual bool supportThreadedLoading() const { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -198,6 +198,16 @@ public:
|
|||||||
virtual u64 getHandle() = 0;
|
virtual u64 getHandle() = 0;
|
||||||
|
|
||||||
virtual void unloadHandle() {}
|
virtual void unloadHandle() {}
|
||||||
|
|
||||||
|
virtual u32 getTextureSize() const { return 0; }
|
||||||
|
|
||||||
|
virtual void threadedReload(void* ptr, void* param) const {}
|
||||||
|
|
||||||
|
virtual void threadedSubImage(void* ptr) const {}
|
||||||
|
|
||||||
|
virtual void cleanThreadedLoader() {}
|
||||||
|
|
||||||
|
virtual int getThreadedLoadTextureCounter() const { return 0; }
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
//! Helper function, helps to get the desired texture creation format from the flags.
|
//! Helper function, helps to get the desired texture creation format from the flags.
|
||||||
|
@ -1186,7 +1186,7 @@ namespace video
|
|||||||
\return The created image.
|
\return The created image.
|
||||||
If you no longer need the image, you should call IImage::drop().
|
If you no longer need the image, you should call IImage::drop().
|
||||||
See IReferenceCounted::drop() for more information. */
|
See IReferenceCounted::drop() for more information. */
|
||||||
virtual IImage* createImageFromFile(io::IReadFile* file) =0;
|
virtual IImage* createImageFromFile(io::IReadFile* file, video::IImageLoader** loader = NULL) =0;
|
||||||
|
|
||||||
//! Writes the provided image to a file.
|
//! Writes the provided image to a file.
|
||||||
/** Requires that there is a suitable image writer registered
|
/** Requires that there is a suitable image writer registered
|
||||||
|
@ -104,9 +104,10 @@ bool breakable (wchar_t c)
|
|||||||
if ((c > 12287 && c < 40960) || //Common CJK words
|
if ((c > 12287 && c < 40960) || //Common CJK words
|
||||||
(c > 44031 && c < 55204) || //Hangul
|
(c > 44031 && c < 55204) || //Hangul
|
||||||
(c > 63743 && c < 64256) || //More Chinese
|
(c > 63743 && c < 64256) || //More Chinese
|
||||||
c == 173 || c == L' ' || c == 0) //Soft hyphen and white space
|
c == 173 || c == L' ' || //Soft hyphen and white space
|
||||||
return true;
|
c == 47 || c == 92) //Slash and blackslash
|
||||||
return false;
|
return true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} // end namespace gui
|
} // end namespace gui
|
||||||
} // end namespace irr
|
} // end namespace irr
|
||||||
|
@ -457,6 +457,10 @@ inline SColor CImage::getPixelBox( s32 x, s32 y, s32 fx, s32 fy, s32 bias ) cons
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CImage::setDeleteMemory(bool val)
|
||||||
|
{
|
||||||
|
DeleteMemory = val;
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace video
|
} // end namespace video
|
||||||
} // end namespace irr
|
} // end namespace irr
|
||||||
|
@ -103,6 +103,8 @@ public:
|
|||||||
//! fills the surface with given color
|
//! fills the surface with given color
|
||||||
virtual void fill(const SColor &color);
|
virtual void fill(const SColor &color);
|
||||||
|
|
||||||
|
virtual void setDeleteMemory(bool val);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
//! assumes format and size has been set and creates the rest
|
//! assumes format and size has been set and creates the rest
|
||||||
|
@ -216,7 +216,7 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
|||||||
|
|
||||||
|
|
||||||
//! creates a surface from the file
|
//! creates a surface from the file
|
||||||
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
|
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file, bool skip_checking) const
|
||||||
{
|
{
|
||||||
SBMPHeader header;
|
SBMPHeader header;
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ public:
|
|||||||
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
||||||
|
|
||||||
//! creates a surface from the file
|
//! creates a surface from the file
|
||||||
virtual IImage* loadImage(io::IReadFile* file) const;
|
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ bool CImageLoaderJPG::isALoadableFileFormat(io::IReadFile* file) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
//! creates a surface from the file
|
//! creates a surface from the file
|
||||||
IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const
|
IImage* CImageLoaderJPG::loadImage(io::IReadFile* file, bool skip_checking) const
|
||||||
{
|
{
|
||||||
#ifndef _IRR_COMPILE_WITH_LIBJPEG_
|
#ifndef _IRR_COMPILE_WITH_LIBJPEG_
|
||||||
os::Printer::log("Can't load as not compiled with _IRR_COMPILE_WITH_LIBJPEG_:", file->getFileName(), ELL_DEBUG);
|
os::Printer::log("Can't load as not compiled with _IRR_COMPILE_WITH_LIBJPEG_:", file->getFileName(), ELL_DEBUG);
|
||||||
|
@ -49,7 +49,7 @@ public:
|
|||||||
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
||||||
|
|
||||||
//! creates a surface from the file
|
//! creates a surface from the file
|
||||||
virtual IImage* loadImage(io::IReadFile* file) const;
|
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -27,14 +27,14 @@ namespace video
|
|||||||
// PNG function for error handling
|
// PNG function for error handling
|
||||||
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
|
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
|
||||||
{
|
{
|
||||||
os::Printer::log("PNG fatal error", msg, ELL_ERROR);
|
printf("PNG fatal error: %s\n", msg);
|
||||||
longjmp(png_jmpbuf(png_ptr), 1);
|
longjmp(png_jmpbuf(png_ptr), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PNG function for warning handling
|
// PNG function for warning handling
|
||||||
static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
|
static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
|
||||||
{
|
{
|
||||||
os::Printer::log("PNG warning", msg, ELL_WARNING);
|
//os::Printer::log("PNG warning", msg, ELL_WARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// PNG function for file reading
|
// PNG function for file reading
|
||||||
@ -86,7 +86,7 @@ bool CImageLoaderPng::isALoadableFileFormat(io::IReadFile* file) const
|
|||||||
|
|
||||||
|
|
||||||
// load in the image data
|
// load in the image data
|
||||||
IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
IImage* CImageLoaderPng::loadImage(io::IReadFile* file, bool skip_checking) const
|
||||||
{
|
{
|
||||||
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
||||||
if (!file)
|
if (!file)
|
||||||
@ -96,27 +96,17 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
|||||||
//Used to point to image rows
|
//Used to point to image rows
|
||||||
u8** RowPointers = 0;
|
u8** RowPointers = 0;
|
||||||
|
|
||||||
png_byte buffer[8];
|
if (skip_checking)
|
||||||
// Read the first few bytes of the PNG file
|
file->seek(8);
|
||||||
if( file->read(buffer, 8) != 8 )
|
else if (!isALoadableFileFormat(file))
|
||||||
{
|
|
||||||
os::Printer::log("LOAD PNG: can't read file\n", file->getFileName(), ELL_ERROR);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it really is a PNG file
|
|
||||||
if( png_sig_cmp(buffer, 0, 8) )
|
|
||||||
{
|
|
||||||
os::Printer::log("LOAD PNG: not really a png\n", file->getFileName(), ELL_ERROR);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate the png read struct
|
// Allocate the png read struct
|
||||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||||
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn);
|
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn);
|
||||||
if (!png_ptr)
|
if (!png_ptr)
|
||||||
{
|
{
|
||||||
os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
|
//os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +114,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
|||||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||||
if (!info_ptr)
|
if (!info_ptr)
|
||||||
{
|
{
|
||||||
os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
|
//os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
|
||||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -184,20 +174,6 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
|||||||
if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA)
|
if (ColorType==PNG_COLOR_TYPE_GRAY || ColorType==PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
png_set_gray_to_rgb(png_ptr);
|
png_set_gray_to_rgb(png_ptr);
|
||||||
|
|
||||||
int intent;
|
|
||||||
const double screen_gamma = 2.2;
|
|
||||||
|
|
||||||
if (png_get_sRGB(png_ptr, info_ptr, &intent))
|
|
||||||
png_set_gamma(png_ptr, screen_gamma, 0.45455);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double image_gamma;
|
|
||||||
if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
|
|
||||||
png_set_gamma(png_ptr, screen_gamma, image_gamma);
|
|
||||||
else
|
|
||||||
png_set_gamma(png_ptr, screen_gamma, 0.45455);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the changes in between, as we need to get the new color type
|
// Update the changes in between, as we need to get the new color type
|
||||||
// for proper processing of the RGBA type
|
// for proper processing of the RGBA type
|
||||||
png_read_update_info(png_ptr, info_ptr);
|
png_read_update_info(png_ptr, info_ptr);
|
||||||
@ -229,7 +205,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
|||||||
image = new CImage(ECF_R8G8B8, core::dimension2d<u32>(Width, Height));
|
image = new CImage(ECF_R8G8B8, core::dimension2d<u32>(Width, Height));
|
||||||
if (!image)
|
if (!image)
|
||||||
{
|
{
|
||||||
os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
|
//os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
|
||||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -238,7 +214,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
|||||||
RowPointers = new png_bytep[Height];
|
RowPointers = new png_bytep[Height];
|
||||||
if (!RowPointers)
|
if (!RowPointers)
|
||||||
{
|
{
|
||||||
os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
|
//os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
|
||||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||||
delete image;
|
delete image;
|
||||||
return 0;
|
return 0;
|
||||||
@ -276,6 +252,26 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
|||||||
#endif // _IRR_COMPILE_WITH_LIBPNG_
|
#endif // _IRR_COMPILE_WITH_LIBPNG_
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core::dimension2du CImageLoaderPng::getImageSize(io::IReadFile* file) const
|
||||||
|
{
|
||||||
|
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
||||||
|
if (!file || !isALoadableFileFormat(file))
|
||||||
|
return core::dimension2du(0, 0);
|
||||||
|
core::dimension2d<u32> dim;
|
||||||
|
file->seek(16);
|
||||||
|
file->read(&dim.Width, 4);
|
||||||
|
file->seek(20);
|
||||||
|
file->read(&dim.Height, 4);
|
||||||
|
file->seek(0);
|
||||||
|
#ifndef __BIG_ENDIAN__
|
||||||
|
dim.Width = os::Byteswap::byteswap(dim.Width);
|
||||||
|
dim.Height = os::Byteswap::byteswap(dim.Height);
|
||||||
|
#endif
|
||||||
|
return dim;
|
||||||
|
#else
|
||||||
|
return core::dimension2du(0, 0);
|
||||||
|
#endif // _IRR_COMPILE_WITH_LIBPNG_
|
||||||
|
}
|
||||||
|
|
||||||
IImageLoader* createImageLoaderPNG()
|
IImageLoader* createImageLoaderPNG()
|
||||||
{
|
{
|
||||||
@ -287,4 +283,3 @@ IImageLoader* createImageLoaderPNG()
|
|||||||
}//end namespace video
|
}//end namespace video
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -25,15 +25,17 @@ class CImageLoaderPng : public IImageLoader
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
//! returns true if the file maybe is able to be loaded by this class
|
//! returns true if the file maybe is able to be loaded by this class
|
||||||
//! based on the file extension (e.g. ".png")
|
//! based on the file extension (e.g. ".png")
|
||||||
virtual bool isALoadableFileExtension(const io::path& filename) const;
|
virtual bool isALoadableFileExtension(const io::path& filename) const;
|
||||||
|
|
||||||
//! returns true if the file maybe is able to be loaded by this class
|
//! returns true if the file maybe is able to be loaded by this class
|
||||||
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
||||||
|
|
||||||
//! creates a surface from the file
|
//! creates a surface from the file
|
||||||
virtual IImage* loadImage(io::IReadFile* file) const;
|
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
|
||||||
|
virtual core::dimension2du getImageSize(io::IReadFile* file) const;
|
||||||
|
virtual bool supportThreadedLoading() const { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,6 +103,7 @@ CNullDriver::CNullDriver(io::IFileSystem* io, const core::dimension2d<u32>& scre
|
|||||||
// DriverAttributes->addInt("MaxGeometryVerticesOut", 0);
|
// DriverAttributes->addInt("MaxGeometryVerticesOut", 0);
|
||||||
// DriverAttributes->addFloat("MaxTextureLODBias", 0.f);
|
// DriverAttributes->addFloat("MaxTextureLODBias", 0.f);
|
||||||
DriverAttributes->addInt("Version", 1);
|
DriverAttributes->addInt("Version", 1);
|
||||||
|
DriverAttributes->setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(2048, 2048));
|
||||||
// DriverAttributes->addInt("ShaderLanguageVersion", 0);
|
// DriverAttributes->addInt("ShaderLanguageVersion", 0);
|
||||||
// DriverAttributes->addInt("AntiAlias", 0);
|
// DriverAttributes->addInt("AntiAlias", 0);
|
||||||
|
|
||||||
@ -1278,7 +1279,7 @@ IImage* CNullDriver::createImageFromFile(const io::path& filename)
|
|||||||
|
|
||||||
|
|
||||||
//! Creates a software image from a file.
|
//! Creates a software image from a file.
|
||||||
IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
|
IImage* CNullDriver::createImageFromFile(io::IReadFile* file, video::IImageLoader** loader)
|
||||||
{
|
{
|
||||||
if (!file)
|
if (!file)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1292,6 +1293,11 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
|
|||||||
{
|
{
|
||||||
if (SurfaceLoader[i]->isALoadableFileExtension(file->getFileName()))
|
if (SurfaceLoader[i]->isALoadableFileExtension(file->getFileName()))
|
||||||
{
|
{
|
||||||
|
if (loader)
|
||||||
|
{
|
||||||
|
*loader = SurfaceLoader[i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// reset file position which might have changed due to previous loadImage calls
|
// reset file position which might have changed due to previous loadImage calls
|
||||||
file->seek(0);
|
file->seek(0);
|
||||||
image = SurfaceLoader[i]->loadImage(file);
|
image = SurfaceLoader[i]->loadImage(file);
|
||||||
@ -1307,6 +1313,11 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
|
|||||||
file->seek(0);
|
file->seek(0);
|
||||||
if (SurfaceLoader[i]->isALoadableFileFormat(file))
|
if (SurfaceLoader[i]->isALoadableFileFormat(file))
|
||||||
{
|
{
|
||||||
|
if (loader)
|
||||||
|
{
|
||||||
|
*loader = SurfaceLoader[i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
file->seek(0);
|
file->seek(0);
|
||||||
image = SurfaceLoader[i]->loadImage(file);
|
image = SurfaceLoader[i]->loadImage(file);
|
||||||
if (image)
|
if (image)
|
||||||
|
@ -348,7 +348,7 @@ namespace video
|
|||||||
virtual IImage* createImageFromFile(const io::path& filename);
|
virtual IImage* createImageFromFile(const io::path& filename);
|
||||||
|
|
||||||
//! Creates a software image from a file.
|
//! Creates a software image from a file.
|
||||||
virtual IImage* createImageFromFile(io::IReadFile* file);
|
virtual IImage* createImageFromFile(io::IReadFile* file, video::IImageLoader** loader = NULL);
|
||||||
|
|
||||||
//! Creates a software image from a byte array.
|
//! Creates a software image from a byte array.
|
||||||
/** \param useForeignMemory: If true, the image will use the data pointer
|
/** \param useForeignMemory: If true, the image will use the data pointer
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Modify this file to change the last-modified date when you add/remove a file.
|
# Modify this file to change the last-modified date when you add/remove a file.
|
||||||
# This will then trigger a new cmake run automatically.
|
# This will then trigger a new cmake run automatically.
|
||||||
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
|
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
|
||||||
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
|
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
|
||||||
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
|
file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
|
||||||
|
@ -102,7 +102,7 @@ int getRAM()
|
|||||||
*/
|
*/
|
||||||
int getNumProcessors()
|
int getNumProcessors()
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#if defined(__linux__) || defined(__CYGWIN__)
|
||||||
return sysconf(_SC_NPROCESSORS_CONF);
|
return sysconf(_SC_NPROCESSORS_CONF);
|
||||||
#endif
|
#endif
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
|
@ -82,6 +82,7 @@ namespace HardwareStats
|
|||||||
// ========================================================================
|
// ========================================================================
|
||||||
void reportHardwareStats();
|
void reportHardwareStats();
|
||||||
const std::string& getOSVersion();
|
const std::string& getOSVersion();
|
||||||
|
int getNumProcessors();
|
||||||
}; // HardwareStats
|
}; // HardwareStats
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -553,6 +553,33 @@ namespace UserConfigParams
|
|||||||
&m_video_group, "Max texture size when high definition textures are "
|
&m_video_group, "Max texture size when high definition textures are "
|
||||||
"disabled"));
|
"disabled"));
|
||||||
|
|
||||||
|
PARAM_PREFIX BoolUserConfigParam m_hq_mipmap
|
||||||
|
PARAM_DEFAULT(BoolUserConfigParam(false, "hq_mipmap",
|
||||||
|
&m_video_group, "Generate mipmap for textures using "
|
||||||
|
"high quality method with SSE"));
|
||||||
|
|
||||||
|
// ---- Recording
|
||||||
|
PARAM_PREFIX GroupUserConfigParam m_recording_group
|
||||||
|
PARAM_DEFAULT( GroupUserConfigParam("Recording", "Recording Settings") );
|
||||||
|
|
||||||
|
PARAM_PREFIX BoolUserConfigParam m_record_bmp
|
||||||
|
PARAM_DEFAULT(BoolUserConfigParam(false, "record_bmp",
|
||||||
|
&m_recording_group, "Record video using uncompressed bitmap, notice: this "
|
||||||
|
"will require a lot of space and fast disk access."));
|
||||||
|
|
||||||
|
PARAM_PREFIX BoolUserConfigParam m_limit_game_fps
|
||||||
|
PARAM_DEFAULT(BoolUserConfigParam(true, "limit_game_fps",
|
||||||
|
&m_recording_group, "Limit game framerate not beyond the fps of recording "
|
||||||
|
"video."));
|
||||||
|
|
||||||
|
PARAM_PREFIX IntUserConfigParam m_record_compression
|
||||||
|
PARAM_DEFAULT(IntUserConfigParam(90, "record_compression",
|
||||||
|
&m_recording_group, "Specify the compression level of recording video"));
|
||||||
|
|
||||||
|
PARAM_PREFIX IntUserConfigParam m_record_fps
|
||||||
|
PARAM_DEFAULT(IntUserConfigParam(30, "record_fps",
|
||||||
|
&m_recording_group, "Specify the fps of recording video"));
|
||||||
|
|
||||||
// ---- Debug - not saved to config file
|
// ---- Debug - not saved to config file
|
||||||
/** If gamepad debugging is enabled. */
|
/** If gamepad debugging is enabled. */
|
||||||
PARAM_PREFIX bool m_unit_testing PARAM_DEFAULT(false);
|
PARAM_PREFIX bool m_unit_testing PARAM_DEFAULT(false);
|
||||||
@ -617,9 +644,6 @@ namespace UserConfigParams
|
|||||||
/** True if graphical profiler should be displayed */
|
/** True if graphical profiler should be displayed */
|
||||||
PARAM_PREFIX bool m_profiler_enabled PARAM_DEFAULT( false );
|
PARAM_PREFIX bool m_profiler_enabled PARAM_DEFAULT( false );
|
||||||
|
|
||||||
/** True if hardware skinning should be enabled */
|
|
||||||
PARAM_PREFIX bool m_hw_skinning_enabled PARAM_DEFAULT( false );
|
|
||||||
|
|
||||||
// not saved to file
|
// not saved to file
|
||||||
|
|
||||||
// ---- Networking
|
// ---- Networking
|
||||||
|
@ -127,8 +127,13 @@ void CameraNormal::smoothMoveCamera(float dt)
|
|||||||
delta2 = 1;
|
delta2 = 1;
|
||||||
|
|
||||||
btTransform btt = m_kart->getTrans();
|
btTransform btt = m_kart->getTrans();
|
||||||
m_kart_position = btt.getOrigin();// m_kart_position + (btt.getOrigin() - m_kart_position) * delta2;
|
m_kart_position = btt.getOrigin();
|
||||||
m_kart_rotation = m_kart_rotation.normalized().slerp(btt.getRotation().normalized(), delta2);
|
btQuaternion q1, q2;
|
||||||
|
q1 = m_kart_rotation.normalized();
|
||||||
|
q2 = btt.getRotation().normalized();
|
||||||
|
if (dot(q1, q2) < 0.0f)
|
||||||
|
q2 = -q2;
|
||||||
|
m_kart_rotation = q1.slerp(q2, delta2);
|
||||||
|
|
||||||
btt.setOrigin(m_kart_position);
|
btt.setOrigin(m_kart_position);
|
||||||
btt.setRotation(m_kart_rotation);
|
btt.setRotation(m_kart_rotation);
|
||||||
|
@ -52,6 +52,7 @@ void CentralVideoSettings::init()
|
|||||||
hasGS = false;
|
hasGS = false;
|
||||||
hasTextureFilterAnisotropic = false;
|
hasTextureFilterAnisotropic = false;
|
||||||
hasTextureSwizzle = false;
|
hasTextureSwizzle = false;
|
||||||
|
hasPixelBufferObject = false;
|
||||||
|
|
||||||
#if defined(USE_GLES2)
|
#if defined(USE_GLES2)
|
||||||
hasBGRA = false;
|
hasBGRA = false;
|
||||||
@ -196,6 +197,11 @@ void CentralVideoSettings::init()
|
|||||||
hasTextureSwizzle = true;
|
hasTextureSwizzle = true;
|
||||||
Log::info("GLDriver", "ARB Texture Swizzle Present");
|
Log::info("GLDriver", "ARB Texture Swizzle Present");
|
||||||
}
|
}
|
||||||
|
if (hasGLExtension("GL_ARB_pixel_buffer_object"))
|
||||||
|
{
|
||||||
|
hasPixelBufferObject = true;
|
||||||
|
Log::info("GLDriver", "ARB Pixel Buffer Object Present");
|
||||||
|
}
|
||||||
// Only unset the high def textures if they are set as default. If the
|
// Only unset the high def textures if they are set as default. If the
|
||||||
// user has enabled them (bit 1 set), then leave them enabled.
|
// user has enabled them (bit 1 set), then leave them enabled.
|
||||||
if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_HIGHDEFINITION_TEXTURES) &&
|
if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_HIGHDEFINITION_TEXTURES) &&
|
||||||
@ -238,7 +244,7 @@ void CentralVideoSettings::init()
|
|||||||
hasTextureStorage = true;
|
hasTextureStorage = true;
|
||||||
hasTextureSwizzle = true;
|
hasTextureSwizzle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_EXPLICIT_ATTRIB_LOCATION) &&
|
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_EXPLICIT_ATTRIB_LOCATION) &&
|
||||||
m_glsl == true)
|
m_glsl == true)
|
||||||
{
|
{
|
||||||
@ -476,4 +482,14 @@ bool CentralVideoSettings::isARBTextureSwizzleUsable() const
|
|||||||
return m_glsl && hasTextureSwizzle;
|
return m_glsl && hasTextureSwizzle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CentralVideoSettings::isARBPixelBufferObjectUsable() const
|
||||||
|
{
|
||||||
|
return hasPixelBufferObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CentralVideoSettings::supportsThreadedTextureLoading() const
|
||||||
|
{
|
||||||
|
return isARBPixelBufferObjectUsable() && isARBBufferStorageUsable() && isARBTextureStorageUsable();
|
||||||
|
}
|
||||||
|
|
||||||
#endif // !SERVER_ONLY
|
#endif // !SERVER_ONLY
|
||||||
|
@ -44,6 +44,7 @@ private:
|
|||||||
bool hasMultiDrawIndirect;
|
bool hasMultiDrawIndirect;
|
||||||
bool hasTextureFilterAnisotropic;
|
bool hasTextureFilterAnisotropic;
|
||||||
bool hasTextureSwizzle;
|
bool hasTextureSwizzle;
|
||||||
|
bool hasPixelBufferObject;
|
||||||
|
|
||||||
#if defined(USE_GLES2)
|
#if defined(USE_GLES2)
|
||||||
bool hasBGRA;
|
bool hasBGRA;
|
||||||
@ -84,6 +85,7 @@ public:
|
|||||||
bool isARBExplicitAttribLocationUsable() const;
|
bool isARBExplicitAttribLocationUsable() const;
|
||||||
bool isEXTTextureFilterAnisotropicUsable() const;
|
bool isEXTTextureFilterAnisotropicUsable() const;
|
||||||
bool isARBTextureSwizzleUsable() const;
|
bool isARBTextureSwizzleUsable() const;
|
||||||
|
bool isARBPixelBufferObjectUsable() const;
|
||||||
|
|
||||||
#if defined(USE_GLES2)
|
#if defined(USE_GLES2)
|
||||||
bool isEXTTextureFormatBGRA8888Usable() const;
|
bool isEXTTextureFormatBGRA8888Usable() const;
|
||||||
@ -98,6 +100,7 @@ public:
|
|||||||
bool supportsComputeShadersFiltering() const;
|
bool supportsComputeShadersFiltering() const;
|
||||||
bool supportsAsyncInstanceUpload() const;
|
bool supportsAsyncInstanceUpload() const;
|
||||||
bool supportsHardwareSkinning() const;
|
bool supportsHardwareSkinning() const;
|
||||||
|
bool supportsThreadedTextureLoading() const;
|
||||||
|
|
||||||
// "Macro" around feature support and user config
|
// "Macro" around feature support and user config
|
||||||
bool isShadowEnabled() const;
|
bool isShadowEnabled() const;
|
||||||
|
120
src/graphics/hq_mipmap_generator.cpp
Normal file
120
src/graphics/hq_mipmap_generator.cpp
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// SuperTuxKart - a fun racing game with go-kart
|
||||||
|
// Copyright (C) 2017 SuperTuxKart-Team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 3
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
|
||||||
|
#include "graphics/hq_mipmap_generator.hpp"
|
||||||
|
#include "graphics/stk_tex_manager.hpp"
|
||||||
|
#undef DUMP_MIPMAP
|
||||||
|
#ifdef DUMP_MIPMAP
|
||||||
|
#include "graphics/irr_driver.hpp"
|
||||||
|
#include "utils/string_utils.hpp"
|
||||||
|
#endif
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#include <mipmap/img.h>
|
||||||
|
#include <mipmap/imgresize.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
HQMipmapGenerator::HQMipmapGenerator(const io::path& name, uint8_t* data,
|
||||||
|
const core::dimension2d<u32>& size,
|
||||||
|
GLuint texture_name, TexConfig* tc)
|
||||||
|
: video::ITexture(name), m_orig_data(data), m_size(size),
|
||||||
|
m_texture_name(texture_name), m_texture_size(0),
|
||||||
|
m_mipmap_data(NULL), m_tex_config(tc)
|
||||||
|
{
|
||||||
|
assert(m_tex_config != NULL);
|
||||||
|
unsigned width = m_size.Width;
|
||||||
|
unsigned height = m_size.Height;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
width = width < 2 ? 1 : width >> 1;
|
||||||
|
height = height < 2 ? 1 : height >> 1;
|
||||||
|
m_mipmap_sizes.emplace_back(core::dimension2du(width, height),
|
||||||
|
m_texture_size);
|
||||||
|
m_texture_size += width * height * 4;
|
||||||
|
if (width == 1 && height == 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_texture_size = unsigned(m_mipmap_sizes.back().second) + 4;
|
||||||
|
m_mipmap_data = malloc(sizeof(imMipmapCascade));
|
||||||
|
} // HQMipmapGenerator
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void HQMipmapGenerator::threadedReload(void* ptr, void* param) const
|
||||||
|
{
|
||||||
|
imReduceOptions options;
|
||||||
|
imReduceSetOptions(&options,
|
||||||
|
m_tex_config->m_normal_map ?
|
||||||
|
IM_REDUCE_FILTER_NORMALMAP: m_tex_config->m_srgb ?
|
||||||
|
IM_REDUCE_FILTER_SRGB : IM_REDUCE_FILTER_LINEAR/*filter*/,
|
||||||
|
2/*hopcount*/, 2.0f/*alpha*/, 1.0f/*amplifynormal*/,
|
||||||
|
0.0f/*normalsustainfactor*/);
|
||||||
|
imMipmapCascade* mm_cascade = (imMipmapCascade*)m_mipmap_data;
|
||||||
|
#ifdef DEBUG
|
||||||
|
int ret = imBuildMipmapCascade(mm_cascade, m_orig_data, m_size.Width,
|
||||||
|
m_size.Height, 1/*layercount*/, 4, m_size.Width * 4, &options, 0);
|
||||||
|
assert(ret == 1);
|
||||||
|
#else
|
||||||
|
imBuildMipmapCascade(mm_cascade, m_orig_data, m_size.Width,
|
||||||
|
m_size.Height, 1/*layercount*/, 4, m_size.Width * 4, &options, 0);
|
||||||
|
#endif
|
||||||
|
for (unsigned int i = 0; i < m_mipmap_sizes.size(); i++)
|
||||||
|
{
|
||||||
|
const unsigned size = m_mipmap_sizes[i].first.getArea() * 4;
|
||||||
|
memcpy((uint8_t*)ptr + m_mipmap_sizes[i].second,
|
||||||
|
mm_cascade->mipmap[i + 1], size);
|
||||||
|
#ifdef DUMP_MIPMAP
|
||||||
|
video::IImage* image = irr_driver->getVideoDriver()
|
||||||
|
->createImageFromData(video::ECF_A8R8G8B8, m_mipmap_sizes[i].first,
|
||||||
|
mm_cascade->mipmap[i + 1], false/*ownForeignMemory*/);
|
||||||
|
irr_driver->getVideoDriver()->writeImageToFile(image, std::string
|
||||||
|
(StringUtils::toString(i) + "_" +
|
||||||
|
StringUtils::getBasename(NamedPath.getPtr())).c_str());
|
||||||
|
image->drop();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} // threadedReload
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void HQMipmapGenerator::threadedSubImage(void* ptr) const
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_texture_name);
|
||||||
|
for (unsigned int i = 0; i < m_mipmap_sizes.size(); i++)
|
||||||
|
{
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, i + 1, 0, 0,
|
||||||
|
m_mipmap_sizes[i].first.Width, m_mipmap_sizes[i].first.Height,
|
||||||
|
GL_BGRA, GL_UNSIGNED_BYTE,
|
||||||
|
(uint8_t*)ptr + m_mipmap_sizes[i].second);
|
||||||
|
}
|
||||||
|
delete this;
|
||||||
|
#endif
|
||||||
|
} // threadedSubImage
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void HQMipmapGenerator::cleanThreadedLoader()
|
||||||
|
{
|
||||||
|
delete[] m_orig_data;
|
||||||
|
imFreeMipmapCascade((imMipmapCascade*)m_mipmap_data);
|
||||||
|
free(m_mipmap_data);
|
||||||
|
} // cleanThreadedLoader
|
||||||
|
|
||||||
|
#endif
|
103
src/graphics/hq_mipmap_generator.hpp
Normal file
103
src/graphics/hq_mipmap_generator.hpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// SuperTuxKart - a fun racing game with go-kart
|
||||||
|
// Copyright (C) 2017 SuperTuxKart-Team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 3
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#ifndef HEADER_HQ_MIPMAP_GENERATOR_HPP
|
||||||
|
#define HEADER_HQ_MIPMAP_GENERATOR_HPP
|
||||||
|
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
|
||||||
|
#include "graphics/gl_headers.hpp"
|
||||||
|
#include "utils/no_copy.hpp"
|
||||||
|
#include "utils/types.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <ITexture.h>
|
||||||
|
|
||||||
|
using namespace irr;
|
||||||
|
struct TexConfig;
|
||||||
|
|
||||||
|
class HQMipmapGenerator : public video::ITexture, NoCopy
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint8_t* m_orig_data;
|
||||||
|
|
||||||
|
core::dimension2d<u32> m_size;
|
||||||
|
|
||||||
|
GLuint m_texture_name;
|
||||||
|
|
||||||
|
unsigned int m_texture_size;
|
||||||
|
|
||||||
|
void* m_mipmap_data;
|
||||||
|
|
||||||
|
TexConfig* m_tex_config;
|
||||||
|
|
||||||
|
std::vector<std::pair<core::dimension2d<u32>, size_t> > m_mipmap_sizes;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
HQMipmapGenerator(const io::path& name, uint8_t* data,
|
||||||
|
const core::dimension2d<u32>& size, GLuint texture_name,
|
||||||
|
TexConfig* tc);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual ~HQMipmapGenerator() {}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void* lock(video::E_TEXTURE_LOCK_MODE mode =
|
||||||
|
video::ETLM_READ_WRITE, u32 mipmap_level = 0)
|
||||||
|
{ return NULL; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void unlock() {}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual const core::dimension2d<u32>& getOriginalSize() const
|
||||||
|
{ return m_size; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual const core::dimension2d<u32>& getSize() const { return m_size; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual video::E_DRIVER_TYPE getDriverType() const
|
||||||
|
{
|
||||||
|
#if defined(USE_GLES2)
|
||||||
|
return video::EDT_OGLES2;
|
||||||
|
#else
|
||||||
|
return video::EDT_OPENGL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual video::ECOLOR_FORMAT getColorFormat() const
|
||||||
|
{ return video::ECF_A8R8G8B8; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual u32 getPitch() const { return 0; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual bool hasMipMaps() const { return false; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void regenerateMipMapLevels(void* mipmap_data = NULL) {}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual u32 getOpenGLTextureName() const { return m_texture_name; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual u64 getHandle() { return 0; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual unsigned int getTextureSize() const { return m_texture_size; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void threadedReload(void* ptr, void* param) const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void threadedSubImage(void* ptr) const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void cleanThreadedLoader();
|
||||||
|
|
||||||
|
}; // HQMipmapGenerator
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -61,6 +61,7 @@
|
|||||||
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
|
#include "states_screens/dialogs/confirm_resolution_dialog.hpp"
|
||||||
#include "states_screens/state_manager.hpp"
|
#include "states_screens/state_manager.hpp"
|
||||||
#include "tracks/track_manager.hpp"
|
#include "tracks/track_manager.hpp"
|
||||||
|
#include "utils/avi_writer.hpp"
|
||||||
#include "utils/constants.hpp"
|
#include "utils/constants.hpp"
|
||||||
#include "utils/log.hpp"
|
#include "utils/log.hpp"
|
||||||
#include "utils/profiler.hpp"
|
#include "utils/profiler.hpp"
|
||||||
@ -146,6 +147,7 @@ IrrDriver::IrrDriver()
|
|||||||
m_last_light_bucket_distance = 0;
|
m_last_light_bucket_distance = 0;
|
||||||
m_clear_color = video::SColor(255, 100, 101, 140);
|
m_clear_color = video::SColor(255, 100, 101, 140);
|
||||||
m_skinning_joint = 0;
|
m_skinning_joint = 0;
|
||||||
|
m_recording = false;
|
||||||
|
|
||||||
} // IrrDriver
|
} // IrrDriver
|
||||||
|
|
||||||
@ -167,6 +169,9 @@ IrrDriver::~IrrDriver()
|
|||||||
#endif
|
#endif
|
||||||
delete m_wind;
|
delete m_wind;
|
||||||
delete m_renderer;
|
delete m_renderer;
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
AVIWriter::kill();
|
||||||
|
#endif
|
||||||
} // ~IrrDriver
|
} // ~IrrDriver
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -716,13 +721,11 @@ void IrrDriver::initDevice()
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
void IrrDriver::setMaxTextureSize()
|
void IrrDriver::setMaxTextureSize()
|
||||||
{
|
{
|
||||||
if( (UserConfigParams::m_high_definition_textures & 0x01) == 0)
|
const unsigned max =
|
||||||
{
|
(UserConfigParams::m_high_definition_textures & 0x01) == 0 ?
|
||||||
io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
|
UserConfigParams::m_max_texture_size : 2048;
|
||||||
att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(
|
io::IAttributes &att = m_video_driver->getNonConstDriverAttributes();
|
||||||
UserConfigParams::m_max_texture_size,
|
att.setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(max, max));
|
||||||
UserConfigParams::m_max_texture_size));
|
|
||||||
}
|
|
||||||
} // setMaxTextureSize
|
} // setMaxTextureSize
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -923,6 +926,9 @@ void IrrDriver::applyResolutionSettings()
|
|||||||
// (we're sure to update main.cpp at some point and forget this one...)
|
// (we're sure to update main.cpp at some point and forget this one...)
|
||||||
VAOManager::getInstance()->kill();
|
VAOManager::getInstance()->kill();
|
||||||
STKTexManager::getInstance()->kill();
|
STKTexManager::getInstance()->kill();
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
AVIWriter::kill();
|
||||||
|
#endif
|
||||||
// initDevice will drop the current device.
|
// initDevice will drop the current device.
|
||||||
if (CVS->isGLSL())
|
if (CVS->isGLSL())
|
||||||
{
|
{
|
||||||
@ -1842,6 +1848,9 @@ void IrrDriver::update(float dt)
|
|||||||
|
|
||||||
PropertyAnimator::get()->update(dt);
|
PropertyAnimator::get()->update(dt);
|
||||||
|
|
||||||
|
STKTexManager::getInstance()
|
||||||
|
->checkThreadedLoadTextures(true/*util_queue_empty*/);
|
||||||
|
|
||||||
World *world = World::getWorld();
|
World *world = World::getWorld();
|
||||||
|
|
||||||
if (world)
|
if (world)
|
||||||
@ -1884,8 +1893,41 @@ void IrrDriver::update(float dt)
|
|||||||
// menu.
|
// menu.
|
||||||
//if(World::getWorld() && World::getWorld()->isRacePhase())
|
//if(World::getWorld() && World::getWorld()->isRacePhase())
|
||||||
// printRenderStats();
|
// printRenderStats();
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
if (m_recording)
|
||||||
|
AVIWriter::getInstance()->captureFrameBufferImage(dt);
|
||||||
|
#endif
|
||||||
} // update
|
} // update
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void IrrDriver::setRecording(bool val)
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
if (!CVS->isARBPixelBufferObjectUsable())
|
||||||
|
{
|
||||||
|
Log::warn("irr_driver", "PBO extension missing, can't record video.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_recording == val)
|
||||||
|
return;
|
||||||
|
m_recording = val;
|
||||||
|
if (m_recording == true)
|
||||||
|
{
|
||||||
|
std::string track_name = World::getWorld() != NULL ?
|
||||||
|
race_manager->getTrackName() : "menu";
|
||||||
|
AVIWriter::setRecordingTarget(file_manager->getScreenshotDir() +
|
||||||
|
track_name);
|
||||||
|
AVIWriter::getInstance()->resetFrameBufferImage();
|
||||||
|
MessageQueue::add(MessageQueue::MT_GENERIC,
|
||||||
|
_("Video recording started."));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AVIWriter::getInstance()->stopRecording();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // setRecording
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
void IrrDriver::requestScreenshot()
|
void IrrDriver::requestScreenshot()
|
||||||
@ -2095,4 +2137,3 @@ GLuint IrrDriver::getDepthStencilTexture()
|
|||||||
return m_renderer->getDepthStencilTexture();
|
return m_renderer->getDepthStencilTexture();
|
||||||
} // getDepthStencilTexture
|
} // getDepthStencilTexture
|
||||||
|
|
||||||
|
|
||||||
|
@ -163,6 +163,7 @@ private:
|
|||||||
bool m_lightviz;
|
bool m_lightviz;
|
||||||
bool m_distortviz;
|
bool m_distortviz;
|
||||||
bool m_boundingboxesviz;
|
bool m_boundingboxesviz;
|
||||||
|
bool m_recording;
|
||||||
|
|
||||||
/** Background colour to reset a buffer. Can be changed by each track. */
|
/** Background colour to reset a buffer. Can be changed by each track. */
|
||||||
irr::video::SColor m_clear_color;
|
irr::video::SColor m_clear_color;
|
||||||
@ -414,6 +415,10 @@ public:
|
|||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
bool getBoundingBoxesViz() { return m_boundingboxesviz; }
|
bool getBoundingBoxesViz() { return m_boundingboxesviz; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
bool isRecording() const { return m_recording; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void setRecording(bool val);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
u32 getRenderPass() { return m_renderpass; }
|
u32 getRenderPass() { return m_renderpass; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
std::vector<LightNode *> getLights() { return m_lights; }
|
std::vector<LightNode *> getLights() { return m_lights; }
|
||||||
|
@ -526,9 +526,9 @@ void Material::install(bool srgb, bool premul_alpha)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_texture = STKTexManager::getInstance()->getTexture
|
TexConfig tc(srgb, premul_alpha, srgb/*mesh_tex*/);
|
||||||
(m_original_full_path, srgb, premul_alpha, false/*set_material*/,
|
m_texture = STKTexManager::getInstance()
|
||||||
srgb/*mesh_tex*/);
|
->getTexture(m_original_full_path, &tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_texture == NULL) return;
|
if (m_texture == NULL) return;
|
||||||
@ -771,9 +771,8 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
|
|||||||
STKTexManager* stm = STKTexManager::getInstance();
|
STKTexManager* stm = STKTexManager::getInstance();
|
||||||
if (m_gloss_map.size() > 0 && CVS->isDefferedEnabled())
|
if (m_gloss_map.size() > 0 && CVS->isDefferedEnabled())
|
||||||
{
|
{
|
||||||
glossytex = stm->getTexture(m_gloss_map, false/*srgb*/,
|
TexConfig gtc(false/*srgb*/, false/*premul_alpha*/);
|
||||||
false/*premul_alpha*/, false/*set_material*/,
|
glossytex = stm->getTexture(m_gloss_map, >c);
|
||||||
true/*mesh_tex*/);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -787,9 +786,11 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
|
|||||||
stm->STKTexManager::getInstance()->getUnicolorTexture(SColor(0, 0, 0, 0));
|
stm->STKTexManager::getInstance()->getUnicolorTexture(SColor(0, 0, 0, 0));
|
||||||
if (m_colorization_mask.size() > 0)
|
if (m_colorization_mask.size() > 0)
|
||||||
{
|
{
|
||||||
|
TexConfig cmtc(false/*srgb*/, false/*premul_alpha*/,
|
||||||
|
true/*mesh_tex*/, false/*set_material*/,
|
||||||
|
true/*color_mask*/);
|
||||||
colorization_mask_tex = stm->getTexture(m_colorization_mask,
|
colorization_mask_tex = stm->getTexture(m_colorization_mask,
|
||||||
false/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
|
&cmtc);
|
||||||
true/*mesh_tex*/, false/*no_upload*/, true/*single_channel*/);
|
|
||||||
}
|
}
|
||||||
m->setTexture(2, colorization_mask_tex);
|
m->setTexture(2, colorization_mask_tex);
|
||||||
}
|
}
|
||||||
@ -845,32 +846,29 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
|
|||||||
m->setTexture(1, glossytex);
|
m->setTexture(1, glossytex);
|
||||||
return;
|
return;
|
||||||
case SHADERTYPE_SPLATTING:
|
case SHADERTYPE_SPLATTING:
|
||||||
tex = stm->getTexture(m_splatting_texture_1,
|
{
|
||||||
true/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
|
TexConfig stc(true/*srgb*/, false/*premul_alpha*/,
|
||||||
true/*mesh_tex*/);
|
true/*mesh_tex*/, false/*set_material*/);
|
||||||
|
tex = stm->getTexture(m_splatting_texture_1, &stc);
|
||||||
m->setTexture(3, tex);
|
m->setTexture(3, tex);
|
||||||
|
|
||||||
if (m_splatting_texture_2.size() > 0)
|
if (m_splatting_texture_2.size() > 0)
|
||||||
{
|
{
|
||||||
tex = stm->getTexture(m_splatting_texture_2,
|
tex = stm->getTexture(m_splatting_texture_2, &stc);
|
||||||
true/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
|
|
||||||
true/*mesh_tex*/);
|
|
||||||
}
|
}
|
||||||
m->setTexture(4, tex);
|
m->setTexture(4, tex);
|
||||||
|
|
||||||
if (m_splatting_texture_3.size() > 0)
|
if (m_splatting_texture_3.size() > 0)
|
||||||
{
|
{
|
||||||
tex = stm->getTexture(m_splatting_texture_3,
|
tex = stm->getTexture(m_splatting_texture_3, &stc);
|
||||||
true/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
|
|
||||||
true/*mesh_tex*/);
|
|
||||||
}
|
}
|
||||||
m->setTexture(5, tex);
|
m->setTexture(5, tex);
|
||||||
|
|
||||||
if (m_splatting_texture_4.size() > 0)
|
if (m_splatting_texture_4.size() > 0)
|
||||||
{
|
{
|
||||||
tex = stm->getTexture(m_splatting_texture_4,
|
TexConfig s4tc(false/*srgb*/, false/*premul_alpha*/,
|
||||||
false/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
|
true/*mesh_tex*/, false/*set_material*/);
|
||||||
true/*mesh_tex*/);
|
tex = stm->getTexture(m_splatting_texture_4, &s4tc);
|
||||||
}
|
}
|
||||||
m->setTexture(6, tex);
|
m->setTexture(6, tex);
|
||||||
m->setTexture(7, glossytex);
|
m->setTexture(7, glossytex);
|
||||||
@ -878,6 +876,7 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
|
|||||||
// Material and shaders
|
// Material and shaders
|
||||||
m->MaterialType = Shaders::getShader(ES_SPLATTING);
|
m->MaterialType = Shaders::getShader(ES_SPLATTING);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
case SHADERTYPE_WATER:
|
case SHADERTYPE_WATER:
|
||||||
m->setTexture(1, irr_driver->getTexture(FileManager::TEXTURE,
|
m->setTexture(1, irr_driver->getTexture(FileManager::TEXTURE,
|
||||||
"waternormals.jpg"));
|
"waternormals.jpg"));
|
||||||
@ -912,9 +911,10 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m
|
|||||||
{
|
{
|
||||||
if (CVS->isDefferedEnabled())
|
if (CVS->isDefferedEnabled())
|
||||||
{
|
{
|
||||||
tex = stm->getTexture(m_normal_map_tex, false/*srgb*/,
|
TexConfig nmtc(false/*srgb*/, false/*premul_alpha*/,
|
||||||
false/*premul_alpha*/, false/*set_material*/,
|
true/*mesh_tex*/, false/*set_material*/,
|
||||||
true/*mesh_tex*/);
|
false/*color_mask*/, true/*normal_map*/);
|
||||||
|
tex = stm->getTexture(m_normal_map_tex, &nmtc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
tex = stm->STKTexManager::getInstance()->getUnicolorTexture(SColor(0, 0, 0, 0));
|
tex = stm->STKTexManager::getInstance()->getUnicolorTexture(SColor(0, 0, 0, 0));
|
||||||
|
@ -49,10 +49,10 @@ static GLuint generateRTT(const core::dimension2du &res, GLint internalFormat, G
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
RTT::RTT(size_t width, size_t height)
|
RTT::RTT(size_t width, size_t height, float rtt_scale)
|
||||||
{
|
{
|
||||||
m_width = width;
|
m_width = width * rtt_scale;
|
||||||
m_height = height;
|
m_height = height * rtt_scale;
|
||||||
m_shadow_FBO = NULL;
|
m_shadow_FBO = NULL;
|
||||||
m_RH_FBO = NULL;
|
m_RH_FBO = NULL;
|
||||||
m_RSM = NULL;
|
m_RSM = NULL;
|
||||||
@ -60,14 +60,13 @@ RTT::RTT(size_t width, size_t height)
|
|||||||
using namespace video;
|
using namespace video;
|
||||||
using namespace core;
|
using namespace core;
|
||||||
|
|
||||||
dimension2du res(int(width * UserConfigParams::m_scale_rtts_factor),
|
dimension2du res(m_width, m_height);
|
||||||
int(height * UserConfigParams::m_scale_rtts_factor) );
|
|
||||||
|
|
||||||
const dimension2du half = res/2;
|
const dimension2du half = res/2;
|
||||||
const dimension2du quarter = res/4;
|
const dimension2du quarter = res/4;
|
||||||
const dimension2du eighth = res/8;
|
const dimension2du eighth = res/8;
|
||||||
|
|
||||||
const u16 shadowside = u16(1024 * UserConfigParams::m_scale_rtts_factor);
|
const u16 shadowside = u16(1024 * rtt_scale);
|
||||||
const dimension2du shadowsize0(shadowside, shadowside);
|
const dimension2du shadowsize0(shadowside, shadowside);
|
||||||
const dimension2du shadowsize1(shadowside / 2, shadowside / 2);
|
const dimension2du shadowsize1(shadowside / 2, shadowside / 2);
|
||||||
const dimension2du shadowsize2(shadowside / 4, shadowside / 4);
|
const dimension2du shadowsize2(shadowside / 4, shadowside / 4);
|
||||||
@ -294,7 +293,12 @@ RTT::RTT(size_t width, size_t height)
|
|||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
getFBO(FBO_COMBINED_DIFFUSE_SPECULAR).bind();
|
getFBO(FBO_COMBINED_DIFFUSE_SPECULAR).bind();
|
||||||
glClearColor(.5, .5, .5, .5);
|
float color = 0.5;
|
||||||
|
#if defined(USE_GLES2)
|
||||||
|
if (!CVS->isDefferedEnabled())
|
||||||
|
color = pow(color, 1. / 2.2);
|
||||||
|
#endif
|
||||||
|
glClearColor(color, color, color, color);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
#if !defined(USE_GLES2)
|
#if !defined(USE_GLES2)
|
||||||
|
@ -144,7 +144,7 @@ enum TypeRTT : unsigned int
|
|||||||
class RTT
|
class RTT
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RTT(size_t width, size_t height);
|
RTT(size_t width, size_t height, float rtt_scale = 1.0f);
|
||||||
~RTT();
|
~RTT();
|
||||||
|
|
||||||
size_t getWidth () const { return m_width ; }
|
size_t getWidth () const { return m_width ; }
|
||||||
|
@ -199,10 +199,8 @@ void ShaderBasedRenderer::uploadLightingData() const
|
|||||||
void ShaderBasedRenderer::computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode,
|
void ShaderBasedRenderer::computeMatrixesAndCameras(scene::ICameraSceneNode *const camnode,
|
||||||
size_t width, size_t height)
|
size_t width, size_t height)
|
||||||
{
|
{
|
||||||
float w = width * UserConfigParams::m_scale_rtts_factor;
|
m_current_screen_size = core::vector2df((float)width, (float)height);
|
||||||
float h = height * UserConfigParams::m_scale_rtts_factor;
|
m_shadow_matrices.computeMatrixesAndCameras(camnode, width, height,
|
||||||
m_current_screen_size = core::vector2df(w, h);
|
|
||||||
m_shadow_matrices.computeMatrixesAndCameras(camnode, int(w), int(h),
|
|
||||||
m_rtts->getDepthStencilTexture());
|
m_rtts->getDepthStencilTexture());
|
||||||
} // computeMatrixesAndCameras
|
} // computeMatrixesAndCameras
|
||||||
|
|
||||||
@ -669,7 +667,8 @@ void ShaderBasedRenderer::onLoadWorld()
|
|||||||
const core::recti &viewport = Camera::getCamera(0)->getViewport();
|
const core::recti &viewport = Camera::getCamera(0)->getViewport();
|
||||||
size_t width = viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X;
|
size_t width = viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X;
|
||||||
size_t height = viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y;
|
size_t height = viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y;
|
||||||
RTT* rtts = new RTT(width, height);
|
RTT* rtts = new RTT(width, height, CVS->isDefferedEnabled() ?
|
||||||
|
UserConfigParams::m_scale_rtts_factor : 1.0f);
|
||||||
setRTT(rtts);
|
setRTT(rtts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -787,9 +786,7 @@ void ShaderBasedRenderer::render(float dt)
|
|||||||
RaceGUIBase *rg = world->getRaceGUI();
|
RaceGUIBase *rg = world->getRaceGUI();
|
||||||
if (rg) rg->update(dt);
|
if (rg) rg->update(dt);
|
||||||
|
|
||||||
bool force_rtt = UserConfigParams::m_scale_rtts_factor != 1.0f;
|
if (!CVS->isDefferedEnabled())
|
||||||
|
|
||||||
if (!CVS->isDefferedEnabled() && !force_rtt)
|
|
||||||
{
|
{
|
||||||
prepareForwardRenderer();
|
prepareForwardRenderer();
|
||||||
}
|
}
|
||||||
@ -803,12 +800,10 @@ void ShaderBasedRenderer::render(float dt)
|
|||||||
oss << "drawAll() for kart " << cam;
|
oss << "drawAll() for kart " << cam;
|
||||||
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60,
|
PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60,
|
||||||
0x00, 0x00);
|
0x00, 0x00);
|
||||||
camera->activate(!CVS->isDefferedEnabled() && !force_rtt);
|
camera->activate(!CVS->isDefferedEnabled());
|
||||||
rg->preRenderCallback(camera); // adjusts start referee
|
rg->preRenderCallback(camera); // adjusts start referee
|
||||||
irr_driver->getSceneManager()->setActiveCamera(camnode);
|
irr_driver->getSceneManager()->setActiveCamera(camnode);
|
||||||
|
|
||||||
const core::recti &viewport = camera->getViewport();
|
|
||||||
|
|
||||||
if (!CVS->isDefferedEnabled())
|
if (!CVS->isDefferedEnabled())
|
||||||
glEnable(GL_FRAMEBUFFER_SRGB);
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
||||||
|
|
||||||
@ -816,12 +811,12 @@ void ShaderBasedRenderer::render(float dt)
|
|||||||
m_lighting_passes.updateLightsInfo(camnode, dt);
|
m_lighting_passes.updateLightsInfo(camnode, dt);
|
||||||
PROFILER_POP_CPU_MARKER();
|
PROFILER_POP_CPU_MARKER();
|
||||||
PROFILER_PUSH_CPU_MARKER("UBO upload", 0x0, 0xFF, 0x0);
|
PROFILER_PUSH_CPU_MARKER("UBO upload", 0x0, 0xFF, 0x0);
|
||||||
computeMatrixesAndCameras(camnode, viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y);
|
computeMatrixesAndCameras(camnode, m_rtts->getWidth(), m_rtts->getHeight());
|
||||||
m_shadow_matrices.updateSunOrthoMatrices();
|
m_shadow_matrices.updateSunOrthoMatrices();
|
||||||
if(CVS->isARBUniformBufferObjectUsable())
|
if(CVS->isARBUniformBufferObjectUsable())
|
||||||
uploadLightingData();
|
uploadLightingData();
|
||||||
PROFILER_POP_CPU_MARKER();
|
PROFILER_POP_CPU_MARKER();
|
||||||
renderScene(camnode, dt, track->hasShadows(), force_rtt);
|
renderScene(camnode, dt, track->hasShadows(), false);
|
||||||
|
|
||||||
if (irr_driver->getBoundingBoxesViz())
|
if (irr_driver->getBoundingBoxesViz())
|
||||||
{
|
{
|
||||||
@ -830,7 +825,7 @@ void ShaderBasedRenderer::render(float dt)
|
|||||||
|
|
||||||
debugPhysics();
|
debugPhysics();
|
||||||
|
|
||||||
if (CVS->isDefferedEnabled() || force_rtt)
|
if (CVS->isDefferedEnabled())
|
||||||
{
|
{
|
||||||
renderPostProcessing(camera);
|
renderPostProcessing(camera);
|
||||||
}
|
}
|
||||||
@ -865,8 +860,8 @@ void ShaderBasedRenderer::render(float dt)
|
|||||||
irr_driver->getActualScreenSize().Height));
|
irr_driver->getActualScreenSize().Height));
|
||||||
|
|
||||||
m_current_screen_size = core::vector2df(
|
m_current_screen_size = core::vector2df(
|
||||||
irr_driver->getActualScreenSize().Width,
|
(float)irr_driver->getActualScreenSize().Width,
|
||||||
irr_driver->getActualScreenSize().Height);
|
(float)irr_driver->getActualScreenSize().Height);
|
||||||
|
|
||||||
for(unsigned int i=0; i<Camera::getNumCameras(); i++)
|
for(unsigned int i=0; i<Camera::getNumCameras(); i++)
|
||||||
{
|
{
|
||||||
|
@ -51,6 +51,62 @@ const std::string& ShaderFilesManager::getHeader()
|
|||||||
return shader_header;
|
return shader_header;
|
||||||
} // getHeader
|
} // getHeader
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void ShaderFilesManager::readFile(const std::string& file,
|
||||||
|
std::ostringstream& code)
|
||||||
|
{
|
||||||
|
std::ifstream stream(file_manager->getShader(file), std::ios::in);
|
||||||
|
|
||||||
|
if (!stream.is_open())
|
||||||
|
{
|
||||||
|
Log::error("ShaderFilesManager", "Can not open '%s'.", file.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string stk_include = "#stk_include";
|
||||||
|
std::string line;
|
||||||
|
|
||||||
|
while (std::getline(stream, line))
|
||||||
|
{
|
||||||
|
const std::size_t pos = line.find(stk_include);
|
||||||
|
|
||||||
|
// load the custom file pointed by the #stk_include directive
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
// find the start "
|
||||||
|
std::size_t pos = line.find("\"");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
Log::error("ShaderFilesManager", "Invalid #stk_include"
|
||||||
|
" line: '%s'.", line.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string filename = line.substr(pos + 1);
|
||||||
|
|
||||||
|
// find the end "
|
||||||
|
pos = filename.find("\"");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
{
|
||||||
|
Log::error("ShaderFilesManager", "Invalid #stk_include"
|
||||||
|
" line: '%s'.", line.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = filename.substr(0, pos);
|
||||||
|
|
||||||
|
// read the whole include file
|
||||||
|
readFile(filename, code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
code << "\n" << line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
/** Loads a single shader. This is NOT cached, use addShaderFile for that.
|
/** Loads a single shader. This is NOT cached, use addShaderFile for that.
|
||||||
* \param file Filename of the shader to load.
|
* \param file Filename of the shader to load.
|
||||||
@ -90,7 +146,9 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
|
|||||||
|
|
||||||
if (CVS->isARBExplicitAttribLocationUsable())
|
if (CVS->isARBExplicitAttribLocationUsable())
|
||||||
{
|
{
|
||||||
|
#if !defined(USE_GLES2)
|
||||||
code << "#extension GL_ARB_explicit_attrib_location : enable\n";
|
code << "#extension GL_ARB_explicit_attrib_location : enable\n";
|
||||||
|
#endif
|
||||||
code << "#define Explicit_Attrib_Location_Usable\n";
|
code << "#define Explicit_Attrib_Location_Usable\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,12 +164,14 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
|
|||||||
code << "#define VSLayer\n";
|
code << "#define VSLayer\n";
|
||||||
if (CVS->needsRGBBindlessWorkaround())
|
if (CVS->needsRGBBindlessWorkaround())
|
||||||
code << "#define SRGBBindlessFix\n";
|
code << "#define SRGBBindlessFix\n";
|
||||||
|
if (CVS->isDefferedEnabled())
|
||||||
|
code << "#define Advanced_Lighting_Enabled\n";
|
||||||
|
|
||||||
#if !defined(USE_GLES2)
|
#if !defined(USE_GLES2)
|
||||||
// shader compilation fails with some drivers if there is no precision
|
// shader compilation fails with some drivers if there is no precision
|
||||||
// qualifier
|
// qualifier
|
||||||
if (type == GL_FRAGMENT_SHADER)
|
if (type == GL_FRAGMENT_SHADER)
|
||||||
code << "precision mediump float;\n";
|
code << "precision highp float;\n";
|
||||||
#else
|
#else
|
||||||
int range[2], precision;
|
int range[2], precision;
|
||||||
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range,
|
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range,
|
||||||
@ -126,69 +186,7 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
|
|||||||
|
|
||||||
code << getHeader();
|
code << getHeader();
|
||||||
|
|
||||||
std::ifstream stream(file_manager->getShader(file), std::ios::in);
|
readFile(file, code);
|
||||||
if (stream.is_open())
|
|
||||||
{
|
|
||||||
const std::string stk_include = "#stk_include";
|
|
||||||
std::string line;
|
|
||||||
|
|
||||||
while (std::getline(stream, line))
|
|
||||||
{
|
|
||||||
const std::size_t pos = line.find(stk_include);
|
|
||||||
|
|
||||||
// load the custom file pointed by the #stk_include directive
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
// find the start "
|
|
||||||
std::size_t pos = line.find("\"");
|
|
||||||
if (pos == std::string::npos)
|
|
||||||
{
|
|
||||||
Log::error("ShaderFilesManager", "Invalid #stk_include"
|
|
||||||
" line: '%s'.", line.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string filename = line.substr(pos + 1);
|
|
||||||
|
|
||||||
// find the end "
|
|
||||||
pos = filename.find("\"");
|
|
||||||
if (pos == std::string::npos)
|
|
||||||
{
|
|
||||||
Log::error("ShaderFilesManager", "Invalid #stk_include"
|
|
||||||
" line: '%s'.", line.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
filename = filename.substr(0, pos);
|
|
||||||
|
|
||||||
// read the whole include file
|
|
||||||
std::ifstream include_stream(file_manager->getShader(filename), std::ios::in);
|
|
||||||
if (!include_stream.is_open())
|
|
||||||
{
|
|
||||||
Log::error("ShaderFilesManager", "Couldn't open included"
|
|
||||||
" shader: '%s'.", filename.c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string include_line = "";
|
|
||||||
while (std::getline(include_stream, include_line))
|
|
||||||
{
|
|
||||||
code << "\n" << include_line;
|
|
||||||
}
|
|
||||||
include_stream.close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
code << "\n" << line;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log::error("ShaderFilesManager", "Can not open '%s'.", file.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
Log::info("ShaderFilesManager", "Compiling shader : %s", file.c_str());
|
Log::info("ShaderFilesManager", "Compiling shader : %s", file.c_str());
|
||||||
const std::string &source = code.str();
|
const std::string &source = code.str();
|
||||||
|
@ -38,6 +38,7 @@ private:
|
|||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
const std::string& getHeader();
|
const std::string& getHeader();
|
||||||
|
void readFile(const std::string& file, std::ostringstream& code);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
@ -163,19 +163,6 @@ void Skybox::generateCubeMapFromTextures()
|
|||||||
assert(img != NULL);
|
assert(img != NULL);
|
||||||
img->copyToScaling(rgba[i], size, size);
|
img->copyToScaling(rgba[i], size, size);
|
||||||
|
|
||||||
#if defined(USE_GLES2)
|
|
||||||
if (CVS->isEXTTextureFormatBGRA8888Usable())
|
|
||||||
{
|
|
||||||
// BGRA image returned by getTextureImage causes black sky in gles
|
|
||||||
for (unsigned int j = 0; j < size * size; j++)
|
|
||||||
{
|
|
||||||
char tmp_val = rgba[i][j * 4];
|
|
||||||
rgba[i][j * 4] = rgba[i][j * 4 + 2];
|
|
||||||
rgba[i][j * 4 + 2] = tmp_val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (i == 2 || i == 3)
|
if (i == 2 || i == 3)
|
||||||
{
|
{
|
||||||
char *tmp = new char[size * size * 4];
|
char *tmp = new char[size * size * 4];
|
||||||
@ -196,7 +183,8 @@ void Skybox::generateCubeMapFromTextures()
|
|||||||
GL_COMPRESSED_SRGB_ALPHA : GL_SRGB_ALPHA;
|
GL_COMPRESSED_SRGB_ALPHA : GL_SRGB_ALPHA;
|
||||||
GLint format = GL_BGRA;
|
GLint format = GL_BGRA;
|
||||||
#else
|
#else
|
||||||
GLint internal_format = GL_RGBA8;
|
GLint internal_format = CVS->isDefferedEnabled() ? GL_SRGB8_ALPHA8
|
||||||
|
: GL_RGBA8;
|
||||||
GLint format = GL_RGBA;
|
GLint format = GL_RGBA;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -565,15 +565,12 @@ void SphericalHarmonics::setTextures(const std::vector<video::ITexture *> &spher
|
|||||||
assert(img != NULL);
|
assert(img != NULL);
|
||||||
img->copyToScaling(sh_rgba[i], sh_w, sh_h);
|
img->copyToScaling(sh_rgba[i], sh_w, sh_h);
|
||||||
#if defined(USE_GLES2)
|
#if defined(USE_GLES2)
|
||||||
if (!CVS->isEXTTextureFormatBGRA8888Usable())
|
// Code here assume color format is BGRA
|
||||||
|
for (unsigned int j = 0; j < sh_w * sh_h; j++)
|
||||||
{
|
{
|
||||||
// Code here assume color format is BGRA
|
char tmp_val = sh_rgba[i][j * 4];
|
||||||
for (unsigned int j = 0; j < sh_w * sh_h; j++)
|
sh_rgba[i][j * 4] = sh_rgba[i][j * 4 + 2];
|
||||||
{
|
sh_rgba[i][j * 4 + 2] = tmp_val;
|
||||||
char tmp_val = sh_rgba[i][j * 4];
|
|
||||||
sh_rgba[i][j * 4] = sh_rgba[i][j * 4 + 2];
|
|
||||||
sh_rgba[i][j * 4 + 2] = tmp_val;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} //for (unsigned i = 0; i < 6; i++)
|
} //for (unsigned i = 0; i < 6; i++)
|
||||||
|
@ -40,9 +40,9 @@ Stars::Stars(AbstractKart *kart)
|
|||||||
m_parent_kart_node = kart->getNode();
|
m_parent_kart_node = kart->getNode();
|
||||||
m_enabled = false;
|
m_enabled = false;
|
||||||
|
|
||||||
|
TexConfig stc(true/*srgb*/, true/*premul_alpha*/);
|
||||||
video::ITexture* texture = STKTexManager::getInstance()->getTexture
|
video::ITexture* texture = STKTexManager::getInstance()->getTexture
|
||||||
("starparticle.png", true/*srgb*/, true/*premul_alpha*/,
|
("starparticle.png", &stc);
|
||||||
false/*set_material*/, true/*mesh_tex*/);
|
|
||||||
|
|
||||||
Material* star_material =
|
Material* star_material =
|
||||||
material_manager->getMaterial("starparticle.png");
|
material_manager->getMaterial("starparticle.png");
|
||||||
|
@ -1044,10 +1044,11 @@ void STKMeshLoader::loadTextures(SB3dMaterial& material) const
|
|||||||
else
|
else
|
||||||
full_path = fs->getFileBasename(B3dTexture->TextureName);
|
full_path = fs->getFileBasename(B3dTexture->TextureName);
|
||||||
|
|
||||||
|
TexConfig mtc(i <= 1 ? true : false/*srgb*/, false/*premul_alpha*/,
|
||||||
|
true/*mesh_tex*/, true/*set_material*/);
|
||||||
video::ITexture* tex =
|
video::ITexture* tex =
|
||||||
STKTexManager::getInstance()->getTexture(full_path.c_str(),
|
STKTexManager::getInstance()->getTexture(full_path.c_str(),
|
||||||
i <= 1 ? true : false/*is_srgb*/, false/*premul_alpha*/,
|
&mtc);
|
||||||
true/*set_material*/);
|
|
||||||
|
|
||||||
material.Material.setTexture(i, tex);
|
material.Material.setTexture(i, tex);
|
||||||
if (material.Textures[i]->Flags & 0x10) // Clamp U
|
if (material.Textures[i]->Flags & 0x10) // Clamp U
|
||||||
|
@ -16,19 +16,107 @@
|
|||||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
#include "graphics/stk_tex_manager.hpp"
|
#include "graphics/stk_tex_manager.hpp"
|
||||||
|
#include "config/hardware_stats.hpp"
|
||||||
|
#include "config/user_config.hpp"
|
||||||
#include "graphics/central_settings.hpp"
|
#include "graphics/central_settings.hpp"
|
||||||
#include "graphics/materials.hpp"
|
#include "graphics/materials.hpp"
|
||||||
|
#include "graphics/threaded_tex_loader.hpp"
|
||||||
#include "graphics/stk_texture.hpp"
|
#include "graphics/stk_texture.hpp"
|
||||||
#include "io/file_manager.hpp"
|
#include "io/file_manager.hpp"
|
||||||
#include "utils/string_utils.hpp"
|
#include "utils/string_utils.hpp"
|
||||||
#include "utils/log.hpp"
|
#include "utils/log.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
STKTexManager::STKTexManager() : m_pbo(0), m_thread_size(0),
|
||||||
|
m_threaded_load_textures_counter(0)
|
||||||
|
{
|
||||||
|
createThreadedTexLoaders();
|
||||||
|
} // STKTexManager
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
STKTexManager::~STKTexManager()
|
STKTexManager::~STKTexManager()
|
||||||
{
|
{
|
||||||
removeTexture(NULL/*texture*/, true/*remove_all*/);
|
removeTexture(NULL/*texture*/, true/*remove_all*/);
|
||||||
|
destroyThreadedTexLoaders();
|
||||||
} // ~STKTexManager
|
} // ~STKTexManager
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void STKTexManager::createThreadedTexLoaders()
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
if (CVS->supportsThreadedTextureLoading())
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&m_threaded_load_textures_mutex, NULL);
|
||||||
|
pthread_cond_init(&m_cond_request, NULL);
|
||||||
|
m_thread_size = HardwareStats::getNumProcessors();
|
||||||
|
if (m_thread_size == 0)
|
||||||
|
m_thread_size = 1;
|
||||||
|
m_thread_size = core::clamp(m_thread_size, 1,
|
||||||
|
UserConfigParams::m_hq_mipmap ? m_thread_size : 3);
|
||||||
|
const unsigned max_tex_size =
|
||||||
|
(UserConfigParams::m_high_definition_textures & 0x01) == 0 ?
|
||||||
|
UserConfigParams::m_max_texture_size : 2048;
|
||||||
|
const unsigned each_capacity = max_tex_size * max_tex_size * 4;
|
||||||
|
const unsigned pbo_size = each_capacity * m_thread_size;
|
||||||
|
Log::info("STKTexManager", "%d thread(s) for texture loading,"
|
||||||
|
" each capacity %d MB.", m_thread_size,
|
||||||
|
each_capacity / 1024 / 1024);
|
||||||
|
if (UserConfigParams::m_hq_mipmap)
|
||||||
|
Log::info("STKTexManager", "High quality mipmap enabled.");
|
||||||
|
glGenBuffers(1, &m_pbo);
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo);
|
||||||
|
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, pbo_size, NULL,
|
||||||
|
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT |
|
||||||
|
GL_MAP_COHERENT_BIT);
|
||||||
|
uint8_t* pbo_ptr = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER,
|
||||||
|
0, pbo_size, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT |
|
||||||
|
GL_MAP_COHERENT_BIT);
|
||||||
|
size_t offset = 0;
|
||||||
|
for (int i = 0; i < m_thread_size; i++)
|
||||||
|
{
|
||||||
|
m_all_tex_loaders.push_back(new ThreadedTexLoader(each_capacity,
|
||||||
|
offset, pbo_ptr + offset, &m_threaded_load_textures_mutex,
|
||||||
|
&m_cond_request, this));
|
||||||
|
offset += each_capacity;
|
||||||
|
}
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // createThreadedTexLoaders
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void STKTexManager::destroyThreadedTexLoaders()
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
if (CVS->supportsThreadedTextureLoading())
|
||||||
|
{
|
||||||
|
STKTexture* delete_ttl = new STKTexture((uint8_t*)NULL, "delete_ttl",
|
||||||
|
0, false, true);
|
||||||
|
for (int i = 0; i < m_thread_size; i++)
|
||||||
|
addThreadedLoadTexture(delete_ttl);
|
||||||
|
for (int i = 0; i < m_thread_size; i++)
|
||||||
|
{
|
||||||
|
if (!m_all_tex_loaders[i]->waitForReadyToDeleted(2.0f))
|
||||||
|
{
|
||||||
|
Log::info("STKTexManager", "ThreadedTexLoader %d not stopping,"
|
||||||
|
"exiting anyway.", i);
|
||||||
|
}
|
||||||
|
delete m_all_tex_loaders[i];
|
||||||
|
}
|
||||||
|
delete delete_ttl;
|
||||||
|
glDeleteBuffers(1, &m_pbo);
|
||||||
|
pthread_mutex_destroy(&m_threaded_load_textures_mutex);
|
||||||
|
pthread_cond_destroy(&m_cond_request);
|
||||||
|
m_pbo = 0;
|
||||||
|
m_thread_size = 0;
|
||||||
|
m_threaded_load_textures_counter = 0;
|
||||||
|
m_all_tex_loaders.clear();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // destroyThreadedTexLoaders
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
STKTexture* STKTexManager::findTextureInFileSystem(const std::string& filename,
|
STKTexture* STKTexManager::findTextureInFileSystem(const std::string& filename,
|
||||||
std::string* full_path)
|
std::string* full_path)
|
||||||
@ -55,10 +143,8 @@ STKTexture* STKTexManager::findTextureInFileSystem(const std::string& filename,
|
|||||||
} // findTextureInFileSystem
|
} // findTextureInFileSystem
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
|
video::ITexture* STKTexManager::getTexture(const std::string& path,
|
||||||
bool premul_alpha,
|
TexConfig* tc, bool no_upload,
|
||||||
bool set_material, bool mesh_tex,
|
|
||||||
bool no_upload, bool single_channel,
|
|
||||||
bool create_if_unfound)
|
bool create_if_unfound)
|
||||||
{
|
{
|
||||||
auto ret = m_all_textures.find(path);
|
auto ret = m_all_textures.find(path);
|
||||||
@ -79,8 +165,7 @@ video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
|
|||||||
if (create_if_unfound)
|
if (create_if_unfound)
|
||||||
{
|
{
|
||||||
new_texture = new STKTexture(full_path.empty() ? path : full_path,
|
new_texture = new STKTexture(full_path.empty() ? path : full_path,
|
||||||
srgb, premul_alpha, set_material, mesh_tex, no_upload,
|
tc, no_upload);
|
||||||
single_channel);
|
|
||||||
if (new_texture->getOpenGLTextureName() == 0 && !no_upload)
|
if (new_texture->getOpenGLTextureName() == 0 && !no_upload)
|
||||||
{
|
{
|
||||||
const char* name = new_texture->getName().getPtr();
|
const char* name = new_texture->getName().getPtr();
|
||||||
@ -95,6 +180,11 @@ video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
|
|||||||
delete new_texture;
|
delete new_texture;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (new_texture->useThreadedLoading())
|
||||||
|
{
|
||||||
|
addThreadedLoadTexture(new_texture);
|
||||||
|
checkThreadedLoadTextures(false/*util_queue_empty*/);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (create_if_unfound && !no_upload)
|
if (create_if_unfound && !no_upload)
|
||||||
@ -204,6 +294,10 @@ core::stringw STKTexManager::reloadTexture(const irr::core::stringw& name)
|
|||||||
if (p.second == NULL || !p.second->isMeshTexture())
|
if (p.second == NULL || !p.second->isMeshTexture())
|
||||||
continue;
|
continue;
|
||||||
p.second->reload();
|
p.second->reload();
|
||||||
|
if (p.second->useThreadedLoading())
|
||||||
|
{
|
||||||
|
addThreadedLoadTexture(p.second);
|
||||||
|
}
|
||||||
Log::info("STKTexManager", "%s reloaded",
|
Log::info("STKTexManager", "%s reloaded",
|
||||||
p.second->getName().getPtr());
|
p.second->getName().getPtr());
|
||||||
}
|
}
|
||||||
@ -226,6 +320,10 @@ core::stringw STKTexManager::reloadTexture(const irr::core::stringw& name)
|
|||||||
if (fname == tex_name || fname == tex_path)
|
if (fname == tex_name || fname == tex_path)
|
||||||
{
|
{
|
||||||
p.second->reload();
|
p.second->reload();
|
||||||
|
if (p.second->useThreadedLoading())
|
||||||
|
{
|
||||||
|
addThreadedLoadTexture(p.second);
|
||||||
|
}
|
||||||
result += tex_name.c_str();
|
result += tex_name.c_str();
|
||||||
result += L" ";
|
result += L" ";
|
||||||
break;
|
break;
|
||||||
@ -271,3 +369,78 @@ void STKTexManager::setTextureErrorMessage(const std::string &error,
|
|||||||
else
|
else
|
||||||
m_texture_error_message = StringUtils::insertValues(error, detail);
|
m_texture_error_message = StringUtils::insertValues(error, detail);
|
||||||
} // setTextureErrorMessage
|
} // setTextureErrorMessage
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void STKTexManager::checkThreadedLoadTextures(bool util_queue_empty)
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
if (!CVS->supportsThreadedTextureLoading()) return;
|
||||||
|
bool uploaded = false;
|
||||||
|
bool empty_queue = false;
|
||||||
|
if (util_queue_empty)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m_threaded_load_textures_mutex);
|
||||||
|
empty_queue = m_threaded_load_textures_counter == 0;
|
||||||
|
pthread_mutex_unlock(&m_threaded_load_textures_mutex);
|
||||||
|
if (empty_queue)
|
||||||
|
{
|
||||||
|
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
|
||||||
|
{
|
||||||
|
if (ttl->lastQueueReady())
|
||||||
|
{
|
||||||
|
ttl->lock();
|
||||||
|
ttl->setFinishLoading();
|
||||||
|
uploaded = true;
|
||||||
|
ttl->unlock(false/*finish_it*/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
checkThreadedLoadTextures(false/*util_queue_empty*/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty_queue && !uploaded)
|
||||||
|
return;
|
||||||
|
uploaded = false;
|
||||||
|
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
|
||||||
|
{
|
||||||
|
ttl->lock();
|
||||||
|
if (ttl->finishedLoading())
|
||||||
|
{
|
||||||
|
if (!uploaded)
|
||||||
|
{
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo);
|
||||||
|
uploaded = true;
|
||||||
|
}
|
||||||
|
ttl->handleCompletedTextures();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ttl->unlock(false/*finish_it*/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (uploaded)
|
||||||
|
{
|
||||||
|
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
GLenum reason = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
|
||||||
|
if (reason != GL_ALREADY_SIGNALED)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
reason = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT,
|
||||||
|
1000000);
|
||||||
|
}
|
||||||
|
while (reason == GL_TIMEOUT_EXPIRED);
|
||||||
|
}
|
||||||
|
glDeleteSync(sync);
|
||||||
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||||
|
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
|
||||||
|
ttl->unlock(true/*finish_it*/);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // checkThreadedLoadTextures
|
||||||
|
@ -23,17 +23,43 @@
|
|||||||
#include "utils/singleton.hpp"
|
#include "utils/singleton.hpp"
|
||||||
|
|
||||||
#include "irrString.h"
|
#include "irrString.h"
|
||||||
|
#include "ITexture.h"
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <cassert>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class STKTexture;
|
class STKTexture;
|
||||||
|
class ThreadedTexLoader;
|
||||||
namespace irr
|
namespace irr
|
||||||
{
|
{
|
||||||
namespace video { class ITexture; class SColor; }
|
namespace video { class SColor; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct TexConfig
|
||||||
|
{
|
||||||
|
bool m_srgb;
|
||||||
|
bool m_premul_alpha;
|
||||||
|
bool m_mesh_tex;
|
||||||
|
bool m_set_material;
|
||||||
|
bool m_colorization_mask;
|
||||||
|
bool m_normal_map;
|
||||||
|
TexConfig(bool srgb = false, bool premul_alpha = false,
|
||||||
|
bool mesh_tex = true, bool set_material = false,
|
||||||
|
bool color_mask = false, bool normal_map = false)
|
||||||
|
{
|
||||||
|
m_srgb = srgb;
|
||||||
|
m_premul_alpha = premul_alpha;
|
||||||
|
m_mesh_tex = mesh_tex;
|
||||||
|
m_set_material = set_material;
|
||||||
|
m_colorization_mask = color_mask;
|
||||||
|
m_normal_map = normal_map;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class STKTexManager : public Singleton<STKTexManager>, NoCopy
|
class STKTexManager : public Singleton<STKTexManager>, NoCopy
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -43,22 +69,43 @@ private:
|
|||||||
* This is used to specify details like: "while loading kart '...'" */
|
* This is used to specify details like: "while loading kart '...'" */
|
||||||
std::string m_texture_error_message;
|
std::string m_texture_error_message;
|
||||||
|
|
||||||
|
std::vector<ThreadedTexLoader*> m_all_tex_loaders;
|
||||||
|
|
||||||
|
GLuint m_pbo;
|
||||||
|
|
||||||
|
int m_thread_size;
|
||||||
|
|
||||||
|
class SmallestTexture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
inline bool operator()(const irr::video::ITexture* a,
|
||||||
|
const irr::video::ITexture* b) const
|
||||||
|
{
|
||||||
|
return a->getTextureSize() > b->getTextureSize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::priority_queue<irr::video::ITexture*,
|
||||||
|
std::vector<irr::video::ITexture*>, SmallestTexture>
|
||||||
|
m_threaded_load_textures;
|
||||||
|
|
||||||
|
int m_threaded_load_textures_counter;
|
||||||
|
|
||||||
|
pthread_mutex_t m_threaded_load_textures_mutex;
|
||||||
|
|
||||||
|
pthread_cond_t m_cond_request;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
STKTexture* findTextureInFileSystem(const std::string& filename,
|
STKTexture* findTextureInFileSystem(const std::string& filename,
|
||||||
std::string* full_path);
|
std::string* full_path);
|
||||||
public:
|
public:
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
STKTexManager() {}
|
STKTexManager();
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
~STKTexManager();
|
~STKTexManager();
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
irr::video::ITexture* getTexture(const std::string& path,
|
irr::video::ITexture* getTexture(const std::string& path,
|
||||||
bool srgb = false,
|
TexConfig* tc = NULL,
|
||||||
bool premul_alpha = false,
|
|
||||||
bool set_material = false,
|
|
||||||
bool mesh_tex = false,
|
|
||||||
bool no_upload = false,
|
bool no_upload = false,
|
||||||
bool single_channel = false,
|
|
||||||
bool create_if_unfound = true);
|
bool create_if_unfound = true);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
irr::video::ITexture* getUnicolorTexture(const irr::video::SColor &c);
|
irr::video::ITexture* getUnicolorTexture(const irr::video::SColor &c);
|
||||||
@ -127,6 +174,35 @@ public:
|
|||||||
return getTexture(filename, std::string(error_message),
|
return getTexture(filename, std::string(error_message),
|
||||||
std::string(detail));
|
std::string(detail));
|
||||||
} // getTexture
|
} // getTexture
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void checkThreadedLoadTextures(bool util_queue_empty);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
irr::video::ITexture* getThreadedLoadTexture()
|
||||||
|
{ return m_threaded_load_textures.top(); }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void setThreadedLoadTextureCounter(int val)
|
||||||
|
{
|
||||||
|
m_threaded_load_textures_counter += val;
|
||||||
|
assert(m_threaded_load_textures_counter >= 0);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void addThreadedLoadTexture(irr::video::ITexture* t)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m_threaded_load_textures_mutex);
|
||||||
|
m_threaded_load_textures.push(t);
|
||||||
|
setThreadedLoadTextureCounter(t->getThreadedLoadTextureCounter());
|
||||||
|
pthread_cond_signal(&m_cond_request);
|
||||||
|
pthread_mutex_unlock(&m_threaded_load_textures_mutex);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void removeThreadedLoadTexture() { m_threaded_load_textures.pop(); }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool isThreadedLoadTexturesEmpty()
|
||||||
|
{ return m_threaded_load_textures.empty(); }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void createThreadedTexLoaders();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void destroyThreadedTexLoaders();
|
||||||
|
|
||||||
}; // STKTexManager
|
}; // STKTexManager
|
||||||
|
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
#include "graphics/stk_texture.hpp"
|
#include "graphics/stk_texture.hpp"
|
||||||
#include "config/user_config.hpp"
|
#include "config/user_config.hpp"
|
||||||
#include "graphics/central_settings.hpp"
|
#include "graphics/central_settings.hpp"
|
||||||
|
#include "graphics/hq_mipmap_generator.hpp"
|
||||||
#include "graphics/irr_driver.hpp"
|
#include "graphics/irr_driver.hpp"
|
||||||
#include "graphics/material.hpp"
|
#include "graphics/material.hpp"
|
||||||
#include "graphics/material_manager.hpp"
|
#include "graphics/material_manager.hpp"
|
||||||
#include "graphics/materials.hpp"
|
#include "graphics/materials.hpp"
|
||||||
|
#include "graphics/stk_tex_manager.hpp"
|
||||||
#include "modes/profile_world.hpp"
|
#include "modes/profile_world.hpp"
|
||||||
#include "utils/log.hpp"
|
#include "utils/log.hpp"
|
||||||
#include "utils/string_utils.hpp"
|
#include "utils/string_utils.hpp"
|
||||||
@ -33,23 +35,27 @@
|
|||||||
static const uint8_t CACHE_VERSION = 1;
|
static const uint8_t CACHE_VERSION = 1;
|
||||||
#endif
|
#endif
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
STKTexture::STKTexture(const std::string& path, bool srgb, bool premul_alpha,
|
STKTexture::STKTexture(const std::string& path, TexConfig* tc, bool no_upload)
|
||||||
bool set_material, bool mesh_tex, bool no_upload,
|
: video::ITexture(path.c_str()), m_texture_handle(0),
|
||||||
bool single_channel)
|
m_single_channel(false), m_tex_config(NULL), m_material(NULL),
|
||||||
: video::ITexture(path.c_str()), m_texture_handle(0), m_srgb(srgb),
|
m_texture_name(0), m_texture_size(0), m_texture_image(NULL),
|
||||||
m_premul_alpha(premul_alpha), m_mesh_texture(mesh_tex),
|
m_file(NULL), m_img_loader(NULL)
|
||||||
m_single_channel(single_channel), m_material(NULL),
|
|
||||||
m_texture_name(0), m_texture_size(0), m_texture_image(NULL)
|
|
||||||
{
|
{
|
||||||
if (set_material)
|
if (tc != NULL)
|
||||||
{
|
{
|
||||||
m_material = material_manager->getMaterialFor(this);
|
m_tex_config = (TexConfig*)malloc(sizeof(TexConfig));
|
||||||
m_mesh_texture = true;
|
memcpy(m_tex_config, tc, sizeof(TexConfig));
|
||||||
|
m_single_channel = m_tex_config->m_colorization_mask;
|
||||||
|
if (m_tex_config->m_set_material)
|
||||||
|
m_material = material_manager->getMaterialFor(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SERVER_ONLY
|
#ifndef SERVER_ONLY
|
||||||
if (!CVS->isGLSL())
|
if (m_tex_config && !CVS->isGLSL())
|
||||||
m_srgb = false;
|
m_tex_config->m_srgb = false;
|
||||||
|
#ifdef USE_GLES2
|
||||||
|
if (m_tex_config && !CVS->isDefferedEnabled())
|
||||||
|
m_tex_config->m_srgb = false;
|
||||||
|
#endif
|
||||||
if (!CVS->isARBTextureSwizzleUsable())
|
if (!CVS->isARBTextureSwizzleUsable())
|
||||||
m_single_channel = false;
|
m_single_channel = false;
|
||||||
#endif
|
#endif
|
||||||
@ -58,24 +64,25 @@ STKTexture::STKTexture(const std::string& path, bool srgb, bool premul_alpha,
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
STKTexture::STKTexture(uint8_t* data, const std::string& name, size_t size,
|
STKTexture::STKTexture(uint8_t* data, const std::string& name, size_t size,
|
||||||
bool single_channel)
|
bool single_channel, bool delete_ttl)
|
||||||
: video::ITexture(name.c_str()), m_texture_handle(0), m_srgb(false),
|
: video::ITexture(name.c_str()), m_texture_handle(0),
|
||||||
m_premul_alpha(false), m_mesh_texture(false),
|
m_single_channel(single_channel), m_tex_config(NULL),
|
||||||
m_single_channel(single_channel), m_material(NULL),
|
m_material(NULL), m_texture_name(0), m_texture_size(0),
|
||||||
m_texture_name(0), m_texture_size(0), m_texture_image(NULL)
|
m_texture_image(NULL), m_file(NULL), m_img_loader(NULL)
|
||||||
{
|
{
|
||||||
m_size.Width = size;
|
m_size.Width = size;
|
||||||
m_size.Height = size;
|
m_size.Height = size;
|
||||||
m_orig_size = m_size;
|
m_orig_size = m_size;
|
||||||
reload(false/*no_upload*/, data);
|
if (!delete_ttl)
|
||||||
|
reload(false/*no_upload*/, data);
|
||||||
} // STKTexture
|
} // STKTexture
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
STKTexture::STKTexture(video::IImage* img, const std::string& name)
|
STKTexture::STKTexture(video::IImage* img, const std::string& name)
|
||||||
: video::ITexture(name.c_str()), m_texture_handle(0), m_srgb(false),
|
: video::ITexture(name.c_str()), m_texture_handle(0),
|
||||||
m_premul_alpha(false), m_mesh_texture(false),
|
m_single_channel(false), m_tex_config(NULL), m_material(NULL),
|
||||||
m_single_channel(false), m_material(NULL), m_texture_name(0),
|
m_texture_name(0), m_texture_size(0), m_texture_image(NULL),
|
||||||
m_texture_size(0), m_texture_image(NULL)
|
m_file(NULL), m_img_loader(NULL)
|
||||||
{
|
{
|
||||||
reload(false/*no_upload*/, NULL/*preload_data*/, img);
|
reload(false/*no_upload*/, NULL/*preload_data*/, img);
|
||||||
} // STKTexture
|
} // STKTexture
|
||||||
@ -92,6 +99,7 @@ STKTexture::~STKTexture()
|
|||||||
#endif // !SERVER_ONLY
|
#endif // !SERVER_ONLY
|
||||||
if (m_texture_image != NULL)
|
if (m_texture_image != NULL)
|
||||||
m_texture_image->drop();
|
m_texture_image->drop();
|
||||||
|
free(m_tex_config);
|
||||||
} // ~STKTexture
|
} // ~STKTexture
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -108,11 +116,10 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifndef SERVER_ONLY
|
#ifndef SERVER_ONLY
|
||||||
irr_driver->getDevice()->getLogger()->setLogLevel(ELL_NONE);
|
|
||||||
|
|
||||||
std::string compressed_texture;
|
std::string compressed_texture;
|
||||||
#if !defined(USE_GLES2)
|
#if !defined(USE_GLES2)
|
||||||
if (!no_upload && m_mesh_texture && CVS->isTextureCompressionEnabled())
|
if (!no_upload && isMeshTexture() && CVS->isTextureCompressionEnabled())
|
||||||
{
|
{
|
||||||
std::string orig_file = NamedPath.getPtr();
|
std::string orig_file = NamedPath.getPtr();
|
||||||
|
|
||||||
@ -157,77 +164,75 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
|||||||
uint8_t* data = preload_data;
|
uint8_t* data = preload_data;
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
{
|
{
|
||||||
orig_img = preload_img ? preload_img :
|
if (preload_img)
|
||||||
irr_driver->getVideoDriver()->createImageFromFile(NamedPath);
|
orig_img = preload_img;
|
||||||
if (orig_img == NULL)
|
else
|
||||||
{
|
{
|
||||||
return;
|
m_file = irr_driver->getDevice()->getFileSystem()
|
||||||
}
|
->createAndOpenFile(NamedPath);
|
||||||
|
if (m_file == NULL)
|
||||||
if (orig_img->getDimension().Width == 0 ||
|
return;
|
||||||
orig_img->getDimension().Height == 0)
|
irr_driver->getVideoDriver()->createImageFromFile(m_file,
|
||||||
{
|
&m_img_loader);
|
||||||
orig_img->drop();
|
if (m_img_loader == NULL)
|
||||||
return;
|
return;
|
||||||
|
m_file->seek(0);
|
||||||
|
m_orig_size = m_img_loader->getImageSize(m_file);
|
||||||
|
if ((!m_material || m_material->getAlphaMask().empty()) &&
|
||||||
|
useThreadedLoading() && !no_upload)
|
||||||
|
{
|
||||||
|
if (m_orig_size.Width == 0 || m_orig_size.Height == 0)
|
||||||
|
{
|
||||||
|
m_file->drop();
|
||||||
|
m_file = NULL;
|
||||||
|
m_img_loader = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
orig_img = m_img_loader->loadImage(m_file);
|
||||||
|
m_file->drop();
|
||||||
|
m_file = NULL;
|
||||||
|
if (orig_img == NULL || orig_img->getDimension().Width == 0 ||
|
||||||
|
orig_img->getDimension().Height == 0)
|
||||||
|
{
|
||||||
|
if (orig_img)
|
||||||
|
orig_img->drop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_img_loader = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
orig_img = resizeImage(orig_img, &m_orig_size, &m_size);
|
orig_img = resizeImage(orig_img, &m_orig_size, &m_size);
|
||||||
applyMask(orig_img);
|
applyMask(orig_img);
|
||||||
data = (uint8_t*)orig_img->lock();
|
data = orig_img ? (uint8_t*)orig_img->lock() : NULL;
|
||||||
if (m_single_channel)
|
if (m_single_channel && !useThreadedLoading())
|
||||||
{
|
{
|
||||||
uint8_t* sc = new uint8_t[m_size.Width * m_size.Height];
|
data = singleChannelConversion(data);
|
||||||
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
|
|
||||||
sc[i] = data[4 * i + 3];
|
|
||||||
orig_img->unlock();
|
orig_img->unlock();
|
||||||
orig_img->drop();
|
orig_img->drop();
|
||||||
orig_img = NULL;
|
orig_img = NULL;
|
||||||
data = sc;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned int w = m_size.Width;
|
const unsigned int w = m_size.Width;
|
||||||
const unsigned int h = m_size.Height;
|
const unsigned int h = m_size.Height;
|
||||||
unsigned int format = m_single_channel ? GL_RED : GL_BGRA;
|
unsigned int format = m_single_channel ? GL_RED : GL_BGRA;
|
||||||
unsigned int internal_format = m_single_channel ? GL_R8 : GL_RGBA;
|
unsigned int internal_format = m_single_channel ? GL_R8 : isSrgb() ?
|
||||||
|
GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||||
|
|
||||||
#if !defined(USE_GLES2)
|
#if !defined(USE_GLES2)
|
||||||
if (m_mesh_texture && CVS->isTextureCompressionEnabled())
|
if (isMeshTexture() && CVS->isTextureCompressionEnabled())
|
||||||
{
|
{
|
||||||
internal_format = m_single_channel ? GL_COMPRESSED_RED_RGTC1 :
|
internal_format = m_single_channel ? GL_COMPRESSED_RED_RGTC1 :
|
||||||
m_srgb ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT :
|
isSrgb() ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT :
|
||||||
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
internal_format =
|
|
||||||
m_single_channel ? GL_R8 : m_srgb ? GL_SRGB_ALPHA : GL_RGBA;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(USE_GLES2)
|
if (!useThreadedLoading())
|
||||||
if (!CVS->isEXTTextureFormatBGRA8888Usable() && !m_single_channel)
|
formatConversion(data, &format, w, h);
|
||||||
{
|
|
||||||
format = GL_RGBA;
|
|
||||||
for (unsigned int i = 0; i < w * h; i++)
|
|
||||||
{
|
|
||||||
uint8_t tmp_val = data[i * 4];
|
|
||||||
data[i * 4] = data[i * 4 + 2];
|
|
||||||
data[i * 4 + 2] = tmp_val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (m_premul_alpha && !m_single_channel)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < w * h; i++)
|
|
||||||
{
|
|
||||||
float alpha = data[4 * i + 3];
|
|
||||||
if (alpha > 0.0f)
|
|
||||||
alpha = pow(alpha / 255.f, 1.f / 2.2f);
|
|
||||||
data[i * 4] = (uint8_t)(data[i * 4] * alpha);
|
|
||||||
data[i * 4 + 1] = (uint8_t)(data[i * 4 + 1] * alpha);
|
|
||||||
data[i * 4 + 2] = (uint8_t)(data[i * 4 + 2] * alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!no_upload)
|
if (!no_upload)
|
||||||
{
|
{
|
||||||
@ -246,17 +251,35 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_ONE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_RED);
|
||||||
}
|
}
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
|
if (useThreadedLoading())
|
||||||
GL_UNSIGNED_BYTE, data);
|
{
|
||||||
|
int levels = 1;
|
||||||
|
int width = w;
|
||||||
|
int height = h;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
width = width < 2 ? 1 : width >> 1;
|
||||||
|
height = height < 2 ? 1 : height >> 1;
|
||||||
|
levels++;
|
||||||
|
if (width == 1 && height == 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
glTexStorage2D(GL_TEXTURE_2D, levels, internal_format, w, h);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
|
||||||
|
GL_UNSIGNED_BYTE, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (!useThreadedLoading())
|
||||||
{
|
{
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format,
|
||||||
GL_UNSIGNED_BYTE, data);
|
GL_UNSIGNED_BYTE, data);
|
||||||
}
|
}
|
||||||
if (orig_img)
|
if (orig_img)
|
||||||
orig_img->unlock();
|
orig_img->unlock();
|
||||||
if (hasMipMaps())
|
if (!useThreadedLoading() && hasMipMaps())
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,54 +296,74 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
|||||||
if (!no_upload)
|
if (!no_upload)
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
irr_driver->getDevice()->getLogger()->setLogLevel(ELL_WARNING);
|
|
||||||
#endif // !SERVER_ONLY
|
#endif // !SERVER_ONLY
|
||||||
} // reload
|
} // reload
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void STKTexture::formatConversion(uint8_t* data, unsigned int* format,
|
||||||
|
unsigned int w, unsigned int h) const
|
||||||
|
{
|
||||||
|
#if defined(USE_GLES2)
|
||||||
|
if (!m_single_channel)
|
||||||
|
{
|
||||||
|
if (format)
|
||||||
|
*format = GL_RGBA;
|
||||||
|
for (unsigned int i = 0; i < w * h; i++)
|
||||||
|
{
|
||||||
|
uint8_t tmp_val = data[i * 4];
|
||||||
|
data[i * 4] = data[i * 4 + 2];
|
||||||
|
data[i * 4 + 2] = tmp_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (isPremulAlpha() && !m_single_channel)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i < w * h; i++)
|
||||||
|
{
|
||||||
|
float alpha = data[4 * i + 3];
|
||||||
|
if (alpha > 0.0f)
|
||||||
|
{
|
||||||
|
alpha /= 255.0f;
|
||||||
|
#if defined(USE_GLES2)
|
||||||
|
if (CVS->isDefferedEnabled())
|
||||||
|
#endif
|
||||||
|
alpha = pow(alpha, 1.0f / 2.2f);
|
||||||
|
}
|
||||||
|
data[i * 4] = (uint8_t)(data[i * 4] * alpha);
|
||||||
|
data[i * 4 + 1] = (uint8_t)(data[i * 4 + 1] * alpha);
|
||||||
|
data[i * 4 + 2] = (uint8_t)(data[i * 4 + 2] * alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // formatConversion
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
|
video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
|
||||||
core::dimension2du* new_img_size,
|
core::dimension2du* orig_size,
|
||||||
core::dimension2du* new_tex_size)
|
core::dimension2du* final_size) const
|
||||||
{
|
{
|
||||||
video::IImage* image = orig_img;
|
video::IImage* image = orig_img;
|
||||||
#ifndef SERVER_ONLY
|
#ifndef SERVER_ONLY
|
||||||
const core::dimension2du& old_size = image->getDimension();
|
if (image == NULL)
|
||||||
core::dimension2du img_size = old_size;
|
assert(orig_size && orig_size->Width > 0 && orig_size->Height > 0);
|
||||||
|
core::dimension2du img_size = image ? image->getDimension() : *orig_size;
|
||||||
const float ratio = float(img_size.Width) / float(img_size.Height);
|
|
||||||
const unsigned int drv_max_size =
|
|
||||||
irr_driver->getVideoDriver()->getMaxTextureSize().Width;
|
|
||||||
|
|
||||||
if ((img_size.Width > drv_max_size) && (ratio >= 1.0f))
|
|
||||||
{
|
|
||||||
img_size.Width = drv_max_size;
|
|
||||||
img_size.Height = (unsigned)(drv_max_size / ratio);
|
|
||||||
}
|
|
||||||
else if (img_size.Height > drv_max_size)
|
|
||||||
{
|
|
||||||
img_size.Height = drv_max_size;
|
|
||||||
img_size.Width = (unsigned)(drv_max_size * ratio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (img_size != old_size)
|
|
||||||
{
|
|
||||||
video::IImage* new_img = irr_driver->getVideoDriver()
|
|
||||||
->createImage(video::ECF_A8R8G8B8, img_size);
|
|
||||||
image->copyToScaling(new_img);
|
|
||||||
image->drop();
|
|
||||||
image = new_img;
|
|
||||||
}
|
|
||||||
|
|
||||||
core::dimension2du tex_size = img_size.getOptimalSize
|
core::dimension2du tex_size = img_size.getOptimalSize
|
||||||
(!irr_driver->getVideoDriver()->queryFeature(video::EVDF_TEXTURE_NPOT));
|
(!irr_driver->getVideoDriver()->queryFeature(video::EVDF_TEXTURE_NPOT));
|
||||||
const core::dimension2du& max_size = irr_driver->getVideoDriver()
|
const core::dimension2du& max_size = irr_driver->getVideoDriver()
|
||||||
->getDriverAttributes().getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
|
->getDriverAttributes().getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
|
||||||
|
|
||||||
if (max_size.Width > 0 && tex_size.Width > max_size.Width)
|
if (tex_size.Width > max_size.Width)
|
||||||
tex_size.Width = max_size.Width;
|
tex_size.Width = max_size.Width;
|
||||||
if (max_size.Height > 0 && tex_size.Height > max_size.Height)
|
if (tex_size.Height > max_size.Height)
|
||||||
tex_size.Height = max_size.Height;
|
tex_size.Height = max_size.Height;
|
||||||
|
|
||||||
|
if (orig_size && final_size)
|
||||||
|
{
|
||||||
|
*orig_size = img_size;
|
||||||
|
*final_size = tex_size;
|
||||||
|
}
|
||||||
|
if (image == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (image->getColorFormat() != video::ECF_A8R8G8B8 ||
|
if (image->getColorFormat() != video::ECF_A8R8G8B8 ||
|
||||||
tex_size != img_size)
|
tex_size != img_size)
|
||||||
{
|
{
|
||||||
@ -334,11 +377,6 @@ video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
|
|||||||
image = new_texture;
|
image = new_texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_img_size && new_tex_size)
|
|
||||||
{
|
|
||||||
*new_img_size = img_size;
|
|
||||||
*new_tex_size = tex_size;
|
|
||||||
}
|
|
||||||
#endif // !SERVER_ONLY
|
#endif // !SERVER_ONLY
|
||||||
return image;
|
return image;
|
||||||
} // resizeImage
|
} // resizeImage
|
||||||
@ -548,3 +586,101 @@ void STKTexture::unloadHandle()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
} // unloadHandle
|
} // unloadHandle
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool STKTexture::useThreadedLoading() const
|
||||||
|
{
|
||||||
|
#ifdef SERVER_ONLY
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
return CVS->supportsThreadedTextureLoading() &&
|
||||||
|
!CVS->isTextureCompressionEnabled() && isMeshTexture() &&
|
||||||
|
m_img_loader && m_img_loader->supportThreadedLoading();
|
||||||
|
#endif
|
||||||
|
} // useThreadedLoading
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void STKTexture::threadedReload(void* ptr, void* param) const
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
video::IImage* orig_img =
|
||||||
|
m_img_loader->loadImage(m_file, true/*skip_checking*/);
|
||||||
|
orig_img = resizeImage(orig_img);
|
||||||
|
uint8_t* data = (uint8_t*)orig_img->lock();
|
||||||
|
if (m_single_channel)
|
||||||
|
{
|
||||||
|
data = singleChannelConversion(data);
|
||||||
|
orig_img->unlock();
|
||||||
|
orig_img->drop();
|
||||||
|
orig_img = NULL;
|
||||||
|
}
|
||||||
|
formatConversion(data, NULL, m_size.Width, m_size.Height);
|
||||||
|
memcpy(ptr, data, m_texture_size);
|
||||||
|
|
||||||
|
if (orig_img)
|
||||||
|
{
|
||||||
|
orig_img->unlock();
|
||||||
|
orig_img->setDeleteMemory(false);
|
||||||
|
orig_img->drop();
|
||||||
|
}
|
||||||
|
if (useHQMipmap())
|
||||||
|
{
|
||||||
|
HQMipmapGenerator* hqmg = new HQMipmapGenerator(NamedPath, data,
|
||||||
|
m_size, m_texture_name, m_tex_config);
|
||||||
|
((STKTexManager*)(param))->addThreadedLoadTexture(hqmg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
delete[] data;
|
||||||
|
#endif
|
||||||
|
} // threadedReload
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void STKTexture::threadedSubImage(void* ptr) const
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_texture_name);
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_size.Width, m_size.Height,
|
||||||
|
m_single_channel ? GL_RED : GL_BGRA, GL_UNSIGNED_BYTE, ptr);
|
||||||
|
if (useHQMipmap())
|
||||||
|
return;
|
||||||
|
if (hasMipMaps())
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
} // threadedSubImage
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void STKTexture::cleanThreadedLoader()
|
||||||
|
{
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
assert(m_file);
|
||||||
|
m_file->drop();
|
||||||
|
m_file = NULL;
|
||||||
|
m_img_loader = NULL;
|
||||||
|
#endif
|
||||||
|
} // cleanThreadedLoader
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool STKTexture::useHQMipmap() const
|
||||||
|
{
|
||||||
|
return !m_single_channel && UserConfigParams::m_hq_mipmap &&
|
||||||
|
m_size.Width > 1 && m_size.Height > 1;
|
||||||
|
} // useHQMipmap
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool STKTexture::isSrgb() const
|
||||||
|
{
|
||||||
|
return m_tex_config && m_tex_config->m_srgb;
|
||||||
|
} // isSrgb
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool STKTexture::isPremulAlpha() const
|
||||||
|
{
|
||||||
|
return m_tex_config && m_tex_config->m_premul_alpha;
|
||||||
|
} // isPremulAlpha
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool STKTexture::isMeshTexture() const
|
||||||
|
{
|
||||||
|
return m_tex_config && m_tex_config->m_mesh_tex;
|
||||||
|
} // isMeshTexture
|
||||||
|
@ -24,8 +24,15 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <ITexture.h>
|
#include <ITexture.h>
|
||||||
|
|
||||||
|
namespace irr
|
||||||
|
{
|
||||||
|
namespace io { class IReadFile; }
|
||||||
|
namespace video { class IImageLoader; }
|
||||||
|
}
|
||||||
|
|
||||||
using namespace irr;
|
using namespace irr;
|
||||||
|
|
||||||
|
struct TexConfig;
|
||||||
class Material;
|
class Material;
|
||||||
|
|
||||||
class STKTexture : public video::ITexture, NoCopy
|
class STKTexture : public video::ITexture, NoCopy
|
||||||
@ -35,7 +42,9 @@ private:
|
|||||||
|
|
||||||
uint64_t m_texture_handle;
|
uint64_t m_texture_handle;
|
||||||
|
|
||||||
bool m_srgb, m_premul_alpha, m_mesh_texture, m_single_channel;
|
bool m_single_channel;
|
||||||
|
|
||||||
|
TexConfig* m_tex_config;
|
||||||
|
|
||||||
Material* m_material;
|
Material* m_material;
|
||||||
|
|
||||||
@ -45,26 +54,44 @@ private:
|
|||||||
|
|
||||||
video::IImage* m_texture_image;
|
video::IImage* m_texture_image;
|
||||||
|
|
||||||
|
io::IReadFile* m_file;
|
||||||
|
|
||||||
|
video::IImageLoader* m_img_loader;
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
video::IImage* resizeImage(video::IImage* orig_img,
|
video::IImage* resizeImage(video::IImage* orig_img,
|
||||||
core::dimension2du* new_img_size = NULL,
|
core::dimension2du* orig_size = NULL,
|
||||||
core::dimension2du* new_tex_size = NULL);
|
core::dimension2du* final_size = NULL) const;
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void applyMask(video::IImage* orig_img);
|
void applyMask(video::IImage* orig_img);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
bool loadCompressedTexture(const std::string& file_name);
|
bool loadCompressedTexture(const std::string& file_name);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void saveCompressedTexture(const std::string& file_name);
|
void saveCompressedTexture(const std::string& file_name);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void formatConversion(uint8_t* data, unsigned int* format, unsigned int w,
|
||||||
|
unsigned int h) const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
uint8_t* singleChannelConversion(uint8_t* data) const
|
||||||
|
{
|
||||||
|
uint8_t* sc = new uint8_t[m_size.Width * m_size.Height];
|
||||||
|
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
|
||||||
|
sc[i] = data[4 * i + 3];
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool useHQMipmap() const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool isSrgb() const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool isPremulAlpha() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
STKTexture(const std::string& path, bool srgb = false,
|
STKTexture(const std::string& path, TexConfig* tc, bool no_upload = false);
|
||||||
bool premul_alpha = false, bool set_material = false,
|
|
||||||
bool mesh_tex = false, bool no_upload = false,
|
|
||||||
bool single_channel = false);
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
STKTexture(uint8_t* data, const std::string& name, size_t size,
|
STKTexture(uint8_t* data, const std::string& name, size_t size,
|
||||||
bool single_channel = false);
|
bool single_channel = false, bool delete_ttl = false);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
STKTexture(video::IImage* img, const std::string& name);
|
STKTexture(video::IImage* img, const std::string& name);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -108,20 +135,27 @@ public:
|
|||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
virtual void unloadHandle();
|
virtual void unloadHandle();
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
bool isSrgb() const { return m_srgb; }
|
virtual unsigned int getTextureSize() const { return m_texture_size; }
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
bool isPremulAlpha() const { return m_premul_alpha; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
bool isMeshTexture() const { return m_mesh_texture; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
void setMeshTexture(bool val) { m_mesh_texture = val; }
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
unsigned int getTextureSize() const { return m_texture_size; }
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
void reload(bool no_upload = false, uint8_t* preload_data = NULL,
|
void reload(bool no_upload = false, uint8_t* preload_data = NULL,
|
||||||
video::IImage* preload_img = NULL);
|
video::IImage* preload_img = NULL);
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
video::IImage* getTextureImage() { return m_texture_image; }
|
video::IImage* getTextureImage() { return m_texture_image; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool useThreadedLoading() const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void threadedReload(void* ptr, void* param) const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void threadedSubImage(void* ptr) const;
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual void cleanThreadedLoader();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
virtual int getThreadedLoadTextureCounter() const
|
||||||
|
{
|
||||||
|
return useHQMipmap() ? 2 : 1;
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool isMeshTexture() const;
|
||||||
|
|
||||||
}; // STKTexture
|
}; // STKTexture
|
||||||
|
|
||||||
|
100
src/graphics/threaded_tex_loader.cpp
Normal file
100
src/graphics/threaded_tex_loader.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// SuperTuxKart - a fun racing game with go-kart
|
||||||
|
// Copyright (C) 2017 SuperTuxKart-Team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 3
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
|
||||||
|
#include "graphics/threaded_tex_loader.hpp"
|
||||||
|
#include "graphics/stk_tex_manager.hpp"
|
||||||
|
#include "utils/string_utils.hpp"
|
||||||
|
#include "utils/vs.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <ITexture.h>
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void* ThreadedTexLoader::startRoutine(void *obj)
|
||||||
|
{
|
||||||
|
ThreadedTexLoader* ttl = (ThreadedTexLoader*)obj;
|
||||||
|
VS::setThreadName((std::string("ThrTexLoader") +
|
||||||
|
StringUtils::toString(ttl->m_pbo_offset / 1024 / 1024)).c_str());
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&ttl->m_mutex);
|
||||||
|
bool finished = ttl->finishedLoading();
|
||||||
|
pthread_mutex_unlock(&ttl->m_mutex);
|
||||||
|
if (finished)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(ttl->m_texture_queue_mutex);
|
||||||
|
bool waiting = ttl->m_stktm->isThreadedLoadTexturesEmpty();
|
||||||
|
ttl->m_last_queue_ready.setAtomic(!ttl->m_completed_textures.empty() &&
|
||||||
|
waiting);
|
||||||
|
while (waiting)
|
||||||
|
{
|
||||||
|
pthread_cond_wait(ttl->m_cond_request, ttl->m_texture_queue_mutex);
|
||||||
|
waiting = ttl->m_stktm->isThreadedLoadTexturesEmpty();
|
||||||
|
}
|
||||||
|
irr::video::ITexture* target_tex =
|
||||||
|
ttl->m_stktm->getThreadedLoadTexture();
|
||||||
|
if (strcmp(target_tex->getName().getPtr(), "delete_ttl") == 0)
|
||||||
|
{
|
||||||
|
ttl->m_stktm->removeThreadedLoadTexture();
|
||||||
|
ttl->m_stktm->setThreadedLoadTextureCounter(-1);
|
||||||
|
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
|
||||||
|
ttl->setCanBeDeleted();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
assert(target_tex->getTextureSize() <= ttl->m_tex_capacity);
|
||||||
|
if (target_tex->getTextureSize() + ttl->m_tex_size_loaded >
|
||||||
|
ttl->m_tex_capacity)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&ttl->m_mutex);
|
||||||
|
ttl->setFinishLoading();
|
||||||
|
pthread_mutex_unlock(&ttl->m_mutex);
|
||||||
|
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ttl->m_stktm->removeThreadedLoadTexture();
|
||||||
|
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
|
||||||
|
target_tex->threadedReload(ttl->m_pbo_ptr + ttl->m_tex_size_loaded,
|
||||||
|
ttl->m_stktm);
|
||||||
|
target_tex->cleanThreadedLoader();
|
||||||
|
ttl->m_tex_size_loaded += target_tex->getTextureSize();
|
||||||
|
ttl->m_completed_textures.push_back(target_tex);
|
||||||
|
pthread_mutex_lock(ttl->m_texture_queue_mutex);
|
||||||
|
ttl->m_stktm->setThreadedLoadTextureCounter(-1);
|
||||||
|
pthread_mutex_unlock(ttl->m_texture_queue_mutex);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
} // startRoutine
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void ThreadedTexLoader::handleCompletedTextures()
|
||||||
|
{
|
||||||
|
assert(m_locked);
|
||||||
|
size_t offset = m_pbo_offset;
|
||||||
|
for (irr::video::ITexture* tex : m_completed_textures)
|
||||||
|
{
|
||||||
|
size_t cur_offset = tex->getTextureSize();
|
||||||
|
tex->threadedSubImage((void*)offset);
|
||||||
|
offset += cur_offset;
|
||||||
|
}
|
||||||
|
m_completed_textures.clear();
|
||||||
|
} // handleCompletedTextures
|
||||||
|
|
||||||
|
#endif
|
118
src/graphics/threaded_tex_loader.hpp
Normal file
118
src/graphics/threaded_tex_loader.hpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// SuperTuxKart - a fun racing game with go-kart
|
||||||
|
// Copyright (C) 2017 SuperTuxKart-Team
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 3
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#ifndef HEADER_THREADED_TEX_LOADER_HPP
|
||||||
|
#define HEADER_THREADED_TEX_LOADER_HPP
|
||||||
|
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
|
||||||
|
#include "utils/can_be_deleted.hpp"
|
||||||
|
#include "utils/no_copy.hpp"
|
||||||
|
#include "utils/synchronised.hpp"
|
||||||
|
#include "utils/types.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace irr
|
||||||
|
{
|
||||||
|
namespace video { class ITexture; }
|
||||||
|
}
|
||||||
|
class STKTexManager;
|
||||||
|
|
||||||
|
class ThreadedTexLoader : public NoCopy, public CanBeDeleted
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const unsigned m_tex_capacity;
|
||||||
|
|
||||||
|
const size_t m_pbo_offset;
|
||||||
|
|
||||||
|
uint8_t* m_pbo_ptr;
|
||||||
|
|
||||||
|
pthread_mutex_t m_mutex;
|
||||||
|
|
||||||
|
pthread_mutex_t* m_texture_queue_mutex;
|
||||||
|
|
||||||
|
pthread_cond_t* m_cond_request;
|
||||||
|
|
||||||
|
STKTexManager* m_stktm;
|
||||||
|
|
||||||
|
unsigned m_tex_size_loaded;
|
||||||
|
|
||||||
|
pthread_t m_thread;
|
||||||
|
|
||||||
|
bool m_finished_loading, m_locked;
|
||||||
|
|
||||||
|
Synchronised<bool> m_last_queue_ready;
|
||||||
|
|
||||||
|
std::vector<irr::video::ITexture*> m_completed_textures;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
static void* startRoutine(void *obj);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
ThreadedTexLoader(unsigned capacity, size_t offset, uint8_t* pbo_ptr,
|
||||||
|
pthread_mutex_t* mutex, pthread_cond_t* cond,
|
||||||
|
STKTexManager* stktm)
|
||||||
|
: m_tex_capacity(capacity), m_pbo_offset(offset),
|
||||||
|
m_pbo_ptr(pbo_ptr), m_texture_queue_mutex(mutex),
|
||||||
|
m_cond_request(cond), m_stktm(stktm),
|
||||||
|
m_tex_size_loaded(0), m_finished_loading(false),
|
||||||
|
m_locked(false), m_last_queue_ready(false)
|
||||||
|
{
|
||||||
|
pthread_mutex_init(&m_mutex, NULL);
|
||||||
|
pthread_create(&m_thread, NULL, &startRoutine, this);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
~ThreadedTexLoader()
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&m_mutex);
|
||||||
|
pthread_join(m_thread, NULL);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool finishedLoading() const { return m_finished_loading; }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void setFinishLoading()
|
||||||
|
{
|
||||||
|
m_last_queue_ready.setAtomic(false);
|
||||||
|
m_finished_loading = true;
|
||||||
|
m_tex_size_loaded = 0;
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool lastQueueReady() const { return m_last_queue_ready.getAtomic(); }
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void handleCompletedTextures();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&m_mutex);
|
||||||
|
m_locked = true;
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void unlock(bool finish_it)
|
||||||
|
{
|
||||||
|
if (!m_locked) return;
|
||||||
|
m_locked = false;
|
||||||
|
if (finish_it)
|
||||||
|
m_finished_loading = false;
|
||||||
|
pthread_mutex_unlock(&m_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // ThreadedTexLoader
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -20,18 +20,25 @@
|
|||||||
|
|
||||||
#include "guiengine/message_queue.hpp"
|
#include "guiengine/message_queue.hpp"
|
||||||
|
|
||||||
#include "config/user_config.hpp"
|
#include "graphics/irr_driver.hpp"
|
||||||
#include "guiengine/engine.hpp"
|
#include "guiengine/engine.hpp"
|
||||||
#include "guiengine/scalable_font.hpp"
|
|
||||||
#include "guiengine/skin.hpp"
|
#include "guiengine/skin.hpp"
|
||||||
|
#include "utils/synchronised.hpp"
|
||||||
|
#include "utils/translation.hpp"
|
||||||
|
|
||||||
#include "IGUIElement.h"
|
#include "IGUIElement.h"
|
||||||
|
#include "IGUIEnvironment.h"
|
||||||
|
#include "IGUIStaticText.h"
|
||||||
|
|
||||||
using namespace GUIEngine;
|
using namespace GUIEngine;
|
||||||
|
|
||||||
namespace MessageQueue
|
namespace MessageQueue
|
||||||
{
|
{
|
||||||
|
// ============================================================================
|
||||||
|
/** The area which the message is drawn. */
|
||||||
|
core::recti g_area;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
/** A small helper class to store and sort messages to be displayed. */
|
/** A small helper class to store and sort messages to be displayed. */
|
||||||
class Message
|
class Message
|
||||||
{
|
{
|
||||||
@ -45,11 +52,15 @@ private:
|
|||||||
* or friend-message::neutral. */
|
* or friend-message::neutral. */
|
||||||
std::string m_render_type;
|
std::string m_render_type;
|
||||||
|
|
||||||
|
/** The text label, can do linebreak if needed. */
|
||||||
|
gui::IGUIStaticText* m_text;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Message(MessageQueue::MessageType mt, const core::stringw &message)
|
Message(MessageQueue::MessageType mt, const core::stringw &message)
|
||||||
{
|
{
|
||||||
m_message_type = mt;
|
m_message_type = mt;
|
||||||
m_message = message;
|
m_message = message;
|
||||||
|
m_text = NULL;
|
||||||
if(mt==MessageQueue::MT_ACHIEVEMENT)
|
if(mt==MessageQueue::MT_ACHIEVEMENT)
|
||||||
m_render_type = "achievement-message::neutral";
|
m_render_type = "achievement-message::neutral";
|
||||||
else if (mt==MessageQueue::MT_ERROR)
|
else if (mt==MessageQueue::MT_ERROR)
|
||||||
@ -60,6 +71,11 @@ public:
|
|||||||
m_render_type = "friend-message::neutral";
|
m_render_type = "friend-message::neutral";
|
||||||
} // Message
|
} // Message
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
~Message()
|
||||||
|
{
|
||||||
|
assert(m_text != NULL);
|
||||||
|
m_text->drop();
|
||||||
|
}
|
||||||
/** Returns the message. */
|
/** Returns the message. */
|
||||||
const core::stringw & getMessage() const { return m_message; }
|
const core::stringw & getMessage() const { return m_message; }
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
@ -72,6 +88,39 @@ public:
|
|||||||
{
|
{
|
||||||
return m_render_type;
|
return m_render_type;
|
||||||
}
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
/** Init the message text, do linebreak as required. */
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
const GUIEngine::BoxRenderParams &brp =
|
||||||
|
GUIEngine::getSkin()->getBoxRenderParams(m_render_type);
|
||||||
|
const unsigned width = irr_driver->getActualScreenSize().Width;
|
||||||
|
const unsigned height = irr_driver->getActualScreenSize().Height;
|
||||||
|
const unsigned max_width = width - (brp.m_left_border +
|
||||||
|
brp.m_right_border);
|
||||||
|
m_text =
|
||||||
|
GUIEngine::getGUIEnv()->addStaticText(m_message.c_str(),
|
||||||
|
core::recti(0, 0, max_width, height));
|
||||||
|
m_text->setRightToLeft(translations->isRTLText(m_message));
|
||||||
|
core::dimension2du dim(m_text->getTextWidth(),
|
||||||
|
m_text->getTextHeight());
|
||||||
|
dim.Width += brp.m_left_border + brp.m_right_border;
|
||||||
|
int x = (width - dim.Width) / 2;
|
||||||
|
int y = height - int(1.5f * dim.Height);
|
||||||
|
g_area = irr::core::recti(x, y, x + dim.Width, y + dim.Height);
|
||||||
|
m_text->setRelativePosition(g_area);
|
||||||
|
m_text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
|
||||||
|
m_text->grab();
|
||||||
|
m_text->remove();
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
/** Draw the message. */
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
assert(m_text != NULL);
|
||||||
|
m_text->draw();
|
||||||
|
}
|
||||||
|
|
||||||
}; // class Message
|
}; // class Message
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@ -88,11 +137,10 @@ public:
|
|||||||
} // operator ()
|
} // operator ()
|
||||||
}; // operator()
|
}; // operator()
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
/** List of all messages. */
|
/** List of all messages. */
|
||||||
std::priority_queue<Message*, std::vector<Message*>,
|
Synchronised<std::priority_queue<Message*, std::vector<Message*>,
|
||||||
CompareMessages> g_all_messages;
|
CompareMessages> > g_all_messages;
|
||||||
|
|
||||||
/** How long the current message has been displayed. The special value
|
/** How long the current message has been displayed. The special value
|
||||||
* -1 indicates that a new message was added when the queue was empty. */
|
* -1 indicates that a new message was added when the queue was empty. */
|
||||||
@ -103,26 +151,18 @@ float g_max_display_time = 5.0f;
|
|||||||
|
|
||||||
/** The label widget used to show the current message. */
|
/** The label widget used to show the current message. */
|
||||||
SkinWidgetContainer *g_container = NULL;
|
SkinWidgetContainer *g_container = NULL;
|
||||||
core::recti g_area;
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void createLabel(const Message *message)
|
void createLabel(Message *message)
|
||||||
{
|
{
|
||||||
if(!g_container)
|
if(!g_container)
|
||||||
g_container = new SkinWidgetContainer();
|
g_container = new SkinWidgetContainer();
|
||||||
|
|
||||||
gui::ScalableFont *font = GUIEngine::getFont();
|
|
||||||
core::dimension2du dim = font->getDimension(message->getMessage().c_str());
|
|
||||||
g_current_display_time = 0.0f;
|
g_current_display_time = 0.0f;
|
||||||
// Maybe make this time dependent on message length as well?
|
// Maybe make this time dependent on message length as well?
|
||||||
g_max_display_time = 5.0f;
|
g_max_display_time = 5.0f;
|
||||||
const GUIEngine::BoxRenderParams &brp =
|
message->init();
|
||||||
GUIEngine::getSkin()->getBoxRenderParams(message->getRenderType());
|
|
||||||
dim.Width +=brp.m_left_border + brp.m_right_border;
|
|
||||||
int x = (UserConfigParams::m_width - dim.Width) / 2;
|
|
||||||
int y = UserConfigParams::m_height - int(1.5f*dim.Height);
|
|
||||||
g_area = irr::core::recti(x, y, x+dim.Width, y+dim.Height);
|
|
||||||
} // createLabel
|
} // createLabel
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -130,9 +170,16 @@ void createLabel(const Message *message)
|
|||||||
* position of the message. */
|
* position of the message. */
|
||||||
void updatePosition()
|
void updatePosition()
|
||||||
{
|
{
|
||||||
if (g_all_messages.empty()) return;
|
g_all_messages.lock();
|
||||||
Message *last = g_all_messages.top();
|
bool empty = g_all_messages.getData().empty();
|
||||||
|
if (empty)
|
||||||
|
{
|
||||||
|
g_all_messages.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Message *last = g_all_messages.getData().top();
|
||||||
createLabel(last);
|
createLabel(last);
|
||||||
|
g_all_messages.unlock();
|
||||||
} // updatePosition
|
} // updatePosition
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -143,13 +190,15 @@ void updatePosition()
|
|||||||
void add(MessageType mt, const irr::core::stringw &message)
|
void add(MessageType mt, const irr::core::stringw &message)
|
||||||
{
|
{
|
||||||
Message *m = new Message(mt, message);
|
Message *m = new Message(mt, message);
|
||||||
if(g_all_messages.empty())
|
g_all_messages.lock();
|
||||||
|
if (g_all_messages.getData().empty())
|
||||||
{
|
{
|
||||||
// Indicate that there is a new message, which should
|
// Indicate that there is a new message, which should
|
||||||
// which needs a new label etc. to be computed.
|
// which needs a new label etc. to be computed.
|
||||||
g_current_display_time =-1.0f;
|
g_current_display_time =-1.0f;
|
||||||
}
|
}
|
||||||
g_all_messages.push(m);
|
g_all_messages.getData().push(m);
|
||||||
|
g_all_messages.unlock();
|
||||||
} // add
|
} // add
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
@ -161,32 +210,38 @@ void add(MessageType mt, const irr::core::stringw &message)
|
|||||||
*/
|
*/
|
||||||
void update(float dt)
|
void update(float dt)
|
||||||
{
|
{
|
||||||
if(g_all_messages.empty()) return;
|
g_all_messages.lock();
|
||||||
|
bool empty = g_all_messages.getData().empty();
|
||||||
|
g_all_messages.unlock();
|
||||||
|
if (empty) return;
|
||||||
|
|
||||||
|
g_all_messages.lock();
|
||||||
g_current_display_time += dt;
|
g_current_display_time += dt;
|
||||||
if(g_current_display_time > g_max_display_time)
|
if (g_current_display_time > g_max_display_time)
|
||||||
{
|
{
|
||||||
Message *last = g_all_messages.top();
|
Message *last = g_all_messages.getData().top();
|
||||||
g_all_messages.pop();
|
g_all_messages.getData().pop();
|
||||||
delete last;
|
delete last;
|
||||||
if(g_all_messages.empty()) return;
|
if (g_all_messages.getData().empty())
|
||||||
|
{
|
||||||
|
g_all_messages.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
g_current_display_time = -1.0f;
|
g_current_display_time = -1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Message *current = g_all_messages.getData().top();
|
||||||
// Create new data for the display.
|
// Create new data for the display.
|
||||||
if(g_current_display_time < 0)
|
if (g_current_display_time < 0)
|
||||||
{
|
{
|
||||||
createLabel(g_all_messages.top());
|
createLabel(current);
|
||||||
}
|
}
|
||||||
|
g_all_messages.unlock();
|
||||||
|
|
||||||
Message *current = g_all_messages.top();
|
|
||||||
GUIEngine::getSkin()->drawMessage(g_container, g_area,
|
GUIEngine::getSkin()->drawMessage(g_container, g_area,
|
||||||
current->getRenderType());
|
current->getRenderType());
|
||||||
gui::ScalableFont *font = GUIEngine::getFont();
|
current->draw();
|
||||||
|
|
||||||
video::SColor color(255, 0, 0, 0);
|
|
||||||
font->draw(current->getMessage(), g_area, color, true, true);
|
|
||||||
|
|
||||||
} // update
|
} // update
|
||||||
|
|
||||||
} // namespace GUIEngine
|
} // namespace GUIEngine
|
||||||
|
@ -342,9 +342,7 @@ video::ITexture* IconButtonWidget::getDeactivatedTexture(video::ITexture* textur
|
|||||||
name += "_disabled";
|
name += "_disabled";
|
||||||
STKTexManager* stkm = STKTexManager::getInstance();
|
STKTexManager* stkm = STKTexManager::getInstance();
|
||||||
STKTexture* disabled_stk_tex = static_cast<STKTexture*>(stkm->getTexture
|
STKTexture* disabled_stk_tex = static_cast<STKTexture*>(stkm->getTexture
|
||||||
(name, false/*srgb*/, false/*premul_alpha*/, false/*set_material*/,
|
(name, NULL/*tc*/, false /*no_upload*/, false/*create_if_unfound*/));
|
||||||
false/*mesh_tex*/, false /*no_upload*/, false/*single_channel*/,
|
|
||||||
false/*create_if_unfound*/));
|
|
||||||
if (disabled_stk_tex == NULL)
|
if (disabled_stk_tex == NULL)
|
||||||
{
|
{
|
||||||
SColor c;
|
SColor c;
|
||||||
|
@ -273,8 +273,18 @@ void InputManager::handleStaticAction(int key, int value)
|
|||||||
case KEY_PRINT:
|
case KEY_PRINT:
|
||||||
// on windows we don't get a press event, only release. So
|
// on windows we don't get a press event, only release. So
|
||||||
// save on release only (to avoid saving twice on other platforms)
|
// save on release only (to avoid saving twice on other platforms)
|
||||||
if (value ==0 )
|
if (value == 0)
|
||||||
irr_driver->requestScreenshot();
|
{
|
||||||
|
if (control_is_pressed)
|
||||||
|
{
|
||||||
|
const bool is_recording = irr_driver->isRecording();
|
||||||
|
irr_driver->setRecording(!is_recording);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
irr_driver->requestScreenshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case KEY_F11:
|
case KEY_F11:
|
||||||
if(value && shift_is_pressed && world && RewindManager::isEnabled())
|
if(value && shift_is_pressed && world && RewindManager::isEnabled())
|
||||||
|
@ -117,7 +117,9 @@ float MainLoop::getLimitedDt()
|
|||||||
// Throttle fps if more than maximum, which can reduce
|
// Throttle fps if more than maximum, which can reduce
|
||||||
// the noise the fan on a graphics card makes.
|
// the noise the fan on a graphics card makes.
|
||||||
// When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus
|
// When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus
|
||||||
const int max_fps = (StateManager::get()->throttleFPS() ? 30 : UserConfigParams::m_max_fps);
|
const int max_fps = (irr_driver->isRecording() &&
|
||||||
|
UserConfigParams::m_limit_game_fps ? UserConfigParams::m_record_fps :
|
||||||
|
StateManager::get()->throttleFPS() ? 60 : UserConfigParams::m_max_fps);
|
||||||
if (dt > 0)
|
if (dt > 0)
|
||||||
{
|
{
|
||||||
const int current_fps = (int)(1000.0f / dt);
|
const int current_fps = (int)(1000.0f / dt);
|
||||||
|
@ -331,13 +331,12 @@ void SoccerWorld::initKartList()
|
|||||||
std::string blue_path =
|
std::string blue_path =
|
||||||
file_manager->getAsset(FileManager::GUI, "soccer_player_blue.png");
|
file_manager->getAsset(FileManager::GUI, "soccer_player_blue.png");
|
||||||
|
|
||||||
|
TexConfig btc(true/*srgb*/, true/*premul_alpha*/);
|
||||||
video::ITexture* red = STKTexManager::getInstance()->getTexture
|
video::ITexture* red = STKTexManager::getInstance()->getTexture
|
||||||
(red_path, true/*srgb*/, true/*premul_alpha*/, false/*set_material*/,
|
(red_path, &btc);
|
||||||
true/*mesh_tex*/);
|
|
||||||
|
|
||||||
video::ITexture* blue = STKTexManager::getInstance()->getTexture
|
video::ITexture* blue = STKTexManager::getInstance()->getTexture
|
||||||
(blue_path, true/*srgb*/, true/*premul_alpha*/, false/*set_material*/,
|
(blue_path, &btc);
|
||||||
true/*mesh_tex*/);
|
|
||||||
|
|
||||||
//Assigning indicators
|
//Assigning indicators
|
||||||
for(unsigned int i = 0; i < kart_amount; i++)
|
for(unsigned int i = 0; i < kart_amount; i++)
|
||||||
|
@ -179,9 +179,9 @@ void ThreeStrikesBattle::kartAdded(AbstractKart* kart, scene::ISceneNode* node)
|
|||||||
// Add heart billboard above it
|
// Add heart billboard above it
|
||||||
std::string heart_path =
|
std::string heart_path =
|
||||||
file_manager->getAsset(FileManager::GUI, "heart.png");
|
file_manager->getAsset(FileManager::GUI, "heart.png");
|
||||||
|
TexConfig btc(true/*srgb*/, true/*premul_alpha*/);
|
||||||
video::ITexture* heart = STKTexManager::getInstance()->getTexture
|
video::ITexture* heart = STKTexManager::getInstance()->getTexture
|
||||||
(heart_path, true/*srgb*/, true/*premul_alpha*/,
|
(heart_path, &btc);
|
||||||
false/*set_material*/, true/*mesh_tex*/);
|
|
||||||
|
|
||||||
float height = kart->getKartHeight() + 0.5f;
|
float height = kart->getKartHeight() + 0.5f;
|
||||||
|
|
||||||
|
@ -61,8 +61,6 @@ void CustomVideoSettingsDialog::beforeAddingWidgets()
|
|||||||
getWidget<CheckBoxWidget>("anim_gfx")->setState(UserConfigParams::m_graphical_effects);
|
getWidget<CheckBoxWidget>("anim_gfx")->setState(UserConfigParams::m_graphical_effects);
|
||||||
getWidget<CheckBoxWidget>("weather_gfx")->setState(UserConfigParams::m_weather_effects);
|
getWidget<CheckBoxWidget>("weather_gfx")->setState(UserConfigParams::m_weather_effects);
|
||||||
getWidget<CheckBoxWidget>("dof")->setState(UserConfigParams::m_dof);
|
getWidget<CheckBoxWidget>("dof")->setState(UserConfigParams::m_dof);
|
||||||
getWidget<CheckBoxWidget>("hd-textures")
|
|
||||||
->setState((UserConfigParams::m_high_definition_textures & 0x01)==0x01);
|
|
||||||
|
|
||||||
SpinnerWidget* kart_anim = getWidget<SpinnerWidget>("steering_animations");
|
SpinnerWidget* kart_anim = getWidget<SpinnerWidget>("steering_animations");
|
||||||
kart_anim->addLabel(_("Disabled")); // 0
|
kart_anim->addLabel(_("Disabled")); // 0
|
||||||
@ -79,33 +77,24 @@ void CustomVideoSettingsDialog::beforeAddingWidgets()
|
|||||||
//I18N: Geometry level disabled : lowest level, no details
|
//I18N: Geometry level disabled : lowest level, no details
|
||||||
geometry_level->addLabel(_("Disabled"));
|
geometry_level->addLabel(_("Disabled"));
|
||||||
//I18N: Geometry level low : few details are displayed
|
//I18N: Geometry level low : few details are displayed
|
||||||
geometry_level->addLabel(_("low"));
|
geometry_level->addLabel(_("Low"));
|
||||||
//I18N: Geometry level high : everything is displayed
|
//I18N: Geometry level high : everything is displayed
|
||||||
geometry_level->addLabel(_("high"));
|
geometry_level->addLabel(_("High"));
|
||||||
geometry_level->setValue(
|
geometry_level->setValue(
|
||||||
UserConfigParams::m_geometry_level == 2 ? 0 :
|
UserConfigParams::m_geometry_level == 2 ? 0 :
|
||||||
UserConfigParams::m_geometry_level == 0 ? 2 : 1);
|
UserConfigParams::m_geometry_level == 0 ? 2 : 1);
|
||||||
|
|
||||||
SpinnerWidget* filtering = getWidget<SpinnerWidget>("filtering");
|
SpinnerWidget* filtering = getWidget<SpinnerWidget>("image_quality");
|
||||||
int value = 0;
|
filtering->addLabel(_("Very Low"));
|
||||||
if (UserConfigParams::m_anisotropic == 2) value = 2;
|
filtering->addLabel(_("Low"));
|
||||||
else if (UserConfigParams::m_anisotropic == 4) value = 3;
|
filtering->addLabel(_("High"));
|
||||||
else if (UserConfigParams::m_anisotropic == 8) value = 4;
|
filtering->addLabel(_("Very High"));
|
||||||
else if (UserConfigParams::m_anisotropic == 16) value = 5;
|
filtering->setValue(OptionsScreenVideo::getImageQuality());
|
||||||
else if (UserConfigParams::m_trilinear) value = 1;
|
|
||||||
filtering->addLabel(_("Bilinear")); // 0
|
|
||||||
filtering->addLabel(_("Trilinear")); // 1
|
|
||||||
filtering->addLabel(_("Anisotropic x2")); // 2
|
|
||||||
filtering->addLabel(_("Anisotropic x4")); // 3
|
|
||||||
filtering->addLabel(_("Anisotropic x8")); // 4
|
|
||||||
filtering->addLabel(_("Anisotropic x16")); // 5
|
|
||||||
|
|
||||||
filtering->setValue(value);
|
|
||||||
|
|
||||||
SpinnerWidget* shadows = getWidget<SpinnerWidget>("shadows");
|
SpinnerWidget* shadows = getWidget<SpinnerWidget>("shadows");
|
||||||
shadows->addLabel(_("Disabled")); // 0
|
shadows->addLabel(_("Disabled")); // 0
|
||||||
shadows->addLabel(_("low")); // 1
|
shadows->addLabel(_("Low")); // 1
|
||||||
shadows->addLabel(_("high")); // 2
|
shadows->addLabel(_("High")); // 2
|
||||||
if (CVS->supportsShadows())
|
if (CVS->supportsShadows())
|
||||||
shadows->setValue(UserConfigParams::m_shadows_resolution / 512);
|
shadows->setValue(UserConfigParams::m_shadows_resolution / 512);
|
||||||
else
|
else
|
||||||
@ -195,11 +184,6 @@ GUIEngine::EventPropagation CustomVideoSettingsDialog::processEvent(const std::s
|
|||||||
UserConfigParams::m_weather_effects =
|
UserConfigParams::m_weather_effects =
|
||||||
getWidget<CheckBoxWidget>("weather_gfx")->getState();
|
getWidget<CheckBoxWidget>("weather_gfx")->getState();
|
||||||
|
|
||||||
// Set bit 0 for enabled/disabled, and set bit 1 to indicate that this
|
|
||||||
// is now a user's choice and should not be overwritten by any default
|
|
||||||
UserConfigParams::m_high_definition_textures =
|
|
||||||
getWidget<CheckBoxWidget>("hd-textures")->getState() ? 0x03 : 0x02;
|
|
||||||
|
|
||||||
UserConfigParams::m_show_steering_animations =
|
UserConfigParams::m_show_steering_animations =
|
||||||
getWidget<SpinnerWidget>("steering_animations")->getValue();
|
getWidget<SpinnerWidget>("steering_animations")->getValue();
|
||||||
|
|
||||||
@ -207,33 +191,8 @@ GUIEngine::EventPropagation CustomVideoSettingsDialog::processEvent(const std::s
|
|||||||
getWidget<SpinnerWidget>("geometry_detail")->getValue();
|
getWidget<SpinnerWidget>("geometry_detail")->getValue();
|
||||||
UserConfigParams::m_geometry_level = val == 2 ? 0 : val == 0 ? 2 : 1;
|
UserConfigParams::m_geometry_level = val == 2 ? 0 : val == 0 ? 2 : 1;
|
||||||
|
|
||||||
switch (getWidget<SpinnerWidget>("filtering")->getValue())
|
OptionsScreenVideo::setImageQuality(getWidget<SpinnerWidget>
|
||||||
{
|
("image_quality")->getValue());
|
||||||
case 0:
|
|
||||||
UserConfigParams::m_anisotropic = 0;
|
|
||||||
UserConfigParams::m_trilinear = false;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
UserConfigParams::m_anisotropic = 0;
|
|
||||||
UserConfigParams::m_trilinear = true;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
UserConfigParams::m_anisotropic = 2;
|
|
||||||
UserConfigParams::m_trilinear = true;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
UserConfigParams::m_anisotropic = 4;
|
|
||||||
UserConfigParams::m_trilinear = true;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
UserConfigParams::m_anisotropic = 8;
|
|
||||||
UserConfigParams::m_trilinear = true;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
UserConfigParams::m_anisotropic = 16;
|
|
||||||
UserConfigParams::m_trilinear = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
user_config->saveConfig();
|
user_config->saveConfig();
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ DebugSliderDialog::DebugSliderDialog() : ModalDialog(0.85f, 0.25f, MODAL_DIALOG_
|
|||||||
loadFromFile("debug_slider.stkgui");
|
loadFromFile("debug_slider.stkgui");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(__APPLE__)
|
|
||||||
void DebugSliderDialog::setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S)
|
void DebugSliderDialog::setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S)
|
||||||
{
|
{
|
||||||
getWidget<SpinnerWidget>(id.c_str())->setValue(G());
|
getWidget<SpinnerWidget>(id.c_str())->setValue(G());
|
||||||
@ -47,7 +46,6 @@ void DebugSliderDialog::setSliderHook(std::string id, unsigned min, unsigned max
|
|||||||
getWidget<SpinnerWidget>(id.c_str())->setMax(max);
|
getWidget<SpinnerWidget>(id.c_str())->setMax(max);
|
||||||
Setters[id] = S;
|
Setters[id] = S;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -66,7 +64,6 @@ void DebugSliderDialog::onEnterPressedInternal()
|
|||||||
|
|
||||||
GUIEngine::EventPropagation DebugSliderDialog::processEvent(const std::string& eventSource)
|
GUIEngine::EventPropagation DebugSliderDialog::processEvent(const std::string& eventSource)
|
||||||
{
|
{
|
||||||
#if !defined(__APPLE__)
|
|
||||||
if (Setters.find(eventSource) == Setters.end())
|
if (Setters.find(eventSource) == Setters.end())
|
||||||
return GUIEngine::EVENT_LET;
|
return GUIEngine::EVENT_LET;
|
||||||
|
|
||||||
@ -74,9 +71,6 @@ GUIEngine::EventPropagation DebugSliderDialog::processEvent(const std::string& e
|
|||||||
Log::info("DebugSlider", "Value for <%s> : %i", eventSource.c_str(), value);
|
Log::info("DebugSlider", "Value for <%s> : %i", eventSource.c_str(), value);
|
||||||
Setters[eventSource](value);
|
Setters[eventSource](value);
|
||||||
return GUIEngine::EVENT_BLOCK;
|
return GUIEngine::EVENT_BLOCK;
|
||||||
#else
|
|
||||||
return GUIEngine::EVENT_LET;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------------
|
||||||
|
@ -33,17 +33,13 @@ class DebugSliderDialog : public GUIEngine::ModalDialog
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
std::string m_id;
|
std::string m_id;
|
||||||
#if !defined(__APPLE__)
|
|
||||||
std::map<std::string, std::function<void(int)> >Setters;
|
std::map<std::string, std::function<void(int)> >Setters;
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DebugSliderDialog();
|
DebugSliderDialog();
|
||||||
|
|
||||||
~DebugSliderDialog() {};
|
~DebugSliderDialog() {};
|
||||||
#if !defined(__APPLE__)
|
|
||||||
void setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S);
|
void setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S);
|
||||||
#endif
|
|
||||||
void changeLabel(std::string id, std::string new_label);
|
void changeLabel(std::string id, std::string new_label);
|
||||||
|
|
||||||
virtual void onEnterPressedInternal() OVERRIDE;
|
virtual void onEnterPressedInternal() OVERRIDE;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "graphics/central_settings.hpp"
|
#include "graphics/central_settings.hpp"
|
||||||
#include "graphics/irr_driver.hpp"
|
#include "graphics/irr_driver.hpp"
|
||||||
#include "graphics/shared_gpu_objects.hpp"
|
#include "graphics/shared_gpu_objects.hpp"
|
||||||
|
#include "graphics/stk_tex_manager.hpp"
|
||||||
#include "guiengine/screen.hpp"
|
#include "guiengine/screen.hpp"
|
||||||
#include "guiengine/widgets/button_widget.hpp"
|
#include "guiengine/widgets/button_widget.hpp"
|
||||||
#include "guiengine/widgets/check_box_widget.hpp"
|
#include "guiengine/widgets/check_box_widget.hpp"
|
||||||
@ -53,32 +54,32 @@ void OptionsScreenVideo::initPresets()
|
|||||||
({
|
({
|
||||||
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
||||||
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */,
|
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */,
|
||||||
false /* animatedScenery */, 0 /* animatedCharacters */, 0 /* anisotropy */,
|
false /* animatedScenery */, 0 /* animatedCharacters */, 0 /* image_quality */,
|
||||||
false /* depth of field */, false /* global illumination */, true /* degraded IBL */, 0 /* hd_textures */
|
false /* depth of field */, false /* global illumination */, true /* degraded IBL */
|
||||||
});
|
});
|
||||||
|
|
||||||
m_presets.push_back
|
m_presets.push_back
|
||||||
({
|
({
|
||||||
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
false /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
||||||
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */,
|
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, false /* weather */,
|
||||||
true /* animatedScenery */, 1 /* animatedCharacters */, 4 /* anisotropy */,
|
true /* animatedScenery */, 1 /* animatedCharacters */, 1 /* image_quality */,
|
||||||
false /* depth of field */, false /* global illumination */, true /* degraded IBL */, 0 /* hd_textures */
|
false /* depth of field */, false /* global illumination */, true /* degraded IBL */
|
||||||
});
|
});
|
||||||
|
|
||||||
m_presets.push_back
|
m_presets.push_back
|
||||||
({
|
({
|
||||||
true /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
true /* light */, 0 /* shadow */, false /* bloom */, false /* motionblur */,
|
||||||
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, true /* weather */,
|
false /* lightshaft */, false /* glow */, false /* mlaa */, false /* ssao */, true /* weather */,
|
||||||
true /* animatedScenery */, 1 /* animatedCharacters */, 4 /* anisotropy */,
|
true /* animatedScenery */, 1 /* animatedCharacters */, 2 /* image_quality */,
|
||||||
false /* depth of field */, false /* global illumination */, true /* degraded IBL */, 1 /* hd_textures */
|
false /* depth of field */, false /* global illumination */, true /* degraded IBL */
|
||||||
});
|
});
|
||||||
|
|
||||||
m_presets.push_back
|
m_presets.push_back
|
||||||
({
|
({
|
||||||
true /* light */, 0 /* shadow */, false /* bloom */, true /* motionblur */,
|
true /* light */, 0 /* shadow */, false /* bloom */, true /* motionblur */,
|
||||||
true /* lightshaft */, true /* glow */, true /* mlaa */, false /* ssao */, true /* weather */,
|
true /* lightshaft */, true /* glow */, true /* mlaa */, false /* ssao */, true /* weather */,
|
||||||
true /* animatedScenery */, 1 /* animatedCharacters */, 8 /* anisotropy */,
|
true /* animatedScenery */, 1 /* animatedCharacters */, 2 /* image_quality */,
|
||||||
false /* depth of field */, false /* global illumination */, false /* degraded IBL */, 1 /* hd_textures */
|
false /* depth of field */, false /* global illumination */, false /* degraded IBL */
|
||||||
});
|
});
|
||||||
|
|
||||||
m_presets.push_back
|
m_presets.push_back
|
||||||
@ -91,8 +92,8 @@ void OptionsScreenVideo::initPresets()
|
|||||||
#else
|
#else
|
||||||
2 /* animatedCharacters */,
|
2 /* animatedCharacters */,
|
||||||
#endif
|
#endif
|
||||||
16 /* anisotropy */,
|
3 /* image_quality */,
|
||||||
true /* depth of field */, false /* global illumination */, false /* degraded IBL */, 1 /* hd_textures */
|
true /* depth of field */, false /* global illumination */, false /* degraded IBL */
|
||||||
});
|
});
|
||||||
|
|
||||||
m_presets.push_back
|
m_presets.push_back
|
||||||
@ -105,8 +106,8 @@ void OptionsScreenVideo::initPresets()
|
|||||||
#else
|
#else
|
||||||
2 /* animatedCharacters */,
|
2 /* animatedCharacters */,
|
||||||
#endif
|
#endif
|
||||||
16 /* anisotropy */,
|
3 /* image_quality */,
|
||||||
true /* depth of field */, true /* global illumination */, false /* degraded IBL */, 1 /* hd_textures */
|
true /* depth of field */, true /* global illumination */, false /* degraded IBL */
|
||||||
});
|
});
|
||||||
|
|
||||||
} // initPresets
|
} // initPresets
|
||||||
@ -139,10 +140,79 @@ struct Resolution
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
int OptionsScreenVideo::getImageQuality()
|
||||||
|
{
|
||||||
|
if (UserConfigParams::m_scale_rtts_factor == 0.8f &&
|
||||||
|
UserConfigParams::m_trilinear == false &&
|
||||||
|
UserConfigParams::m_anisotropic == 0 &&
|
||||||
|
(UserConfigParams::m_high_definition_textures & 0x01) == 0x00 &&
|
||||||
|
UserConfigParams::m_hq_mipmap == false)
|
||||||
|
return 0;
|
||||||
|
if (UserConfigParams::m_scale_rtts_factor == 1.0f &&
|
||||||
|
UserConfigParams::m_trilinear == true &&
|
||||||
|
UserConfigParams::m_anisotropic == 2 &&
|
||||||
|
(UserConfigParams::m_high_definition_textures & 0x01) == 0x00 &&
|
||||||
|
UserConfigParams::m_hq_mipmap == false)
|
||||||
|
return 1;
|
||||||
|
if (UserConfigParams::m_scale_rtts_factor == 1.0f &&
|
||||||
|
UserConfigParams::m_trilinear == true &&
|
||||||
|
UserConfigParams::m_anisotropic == 4 &&
|
||||||
|
(UserConfigParams::m_high_definition_textures & 0x01) == 0x01 &&
|
||||||
|
UserConfigParams::m_hq_mipmap == false)
|
||||||
|
return 2;
|
||||||
|
if (UserConfigParams::m_scale_rtts_factor == 1.0f &&
|
||||||
|
UserConfigParams::m_trilinear == true &&
|
||||||
|
UserConfigParams::m_anisotropic == 16 &&
|
||||||
|
(UserConfigParams::m_high_definition_textures & 0x01) == 0x01 &&
|
||||||
|
UserConfigParams::m_hq_mipmap == true)
|
||||||
|
return 3;
|
||||||
|
return 2;
|
||||||
|
} // getImageQuality
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void OptionsScreenVideo::setImageQuality(int quality)
|
||||||
|
{
|
||||||
|
switch (quality)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
UserConfigParams::m_scale_rtts_factor = 0.8f;
|
||||||
|
UserConfigParams::m_trilinear = false;
|
||||||
|
UserConfigParams::m_anisotropic = 0;
|
||||||
|
UserConfigParams::m_high_definition_textures = 0x02;
|
||||||
|
UserConfigParams::m_hq_mipmap = false;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
UserConfigParams::m_scale_rtts_factor = 1.0f;
|
||||||
|
UserConfigParams::m_trilinear = true;
|
||||||
|
UserConfigParams::m_anisotropic = 2;
|
||||||
|
UserConfigParams::m_high_definition_textures = 0x02;
|
||||||
|
UserConfigParams::m_hq_mipmap = false;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
UserConfigParams::m_scale_rtts_factor = 1.0f;
|
||||||
|
UserConfigParams::m_trilinear = true;
|
||||||
|
UserConfigParams::m_anisotropic = 4;
|
||||||
|
UserConfigParams::m_high_definition_textures = 0x03;
|
||||||
|
UserConfigParams::m_hq_mipmap = false;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
UserConfigParams::m_scale_rtts_factor = 1.0f;
|
||||||
|
UserConfigParams::m_trilinear = true;
|
||||||
|
UserConfigParams::m_anisotropic = 16;
|
||||||
|
UserConfigParams::m_high_definition_textures = 0x03;
|
||||||
|
UserConfigParams::m_hq_mipmap = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
} // setImageQuality
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
OptionsScreenVideo::OptionsScreenVideo() : Screen("options_video.stkgui"),
|
OptionsScreenVideo::OptionsScreenVideo() : Screen("options_video.stkgui"),
|
||||||
m_prev_adv_pipline(false)
|
m_prev_adv_pipline(false),
|
||||||
|
m_prev_img_quality(-1)
|
||||||
{
|
{
|
||||||
m_inited = false;
|
m_inited = false;
|
||||||
initPresets();
|
initPresets();
|
||||||
@ -168,6 +238,7 @@ void OptionsScreenVideo::init()
|
|||||||
{
|
{
|
||||||
Screen::init();
|
Screen::init();
|
||||||
m_prev_adv_pipline = UserConfigParams::m_dynamic_lights;
|
m_prev_adv_pipline = UserConfigParams::m_dynamic_lights;
|
||||||
|
m_prev_img_quality = getImageQuality();
|
||||||
RibbonWidget* ribbon = getWidget<RibbonWidget>("options_choice");
|
RibbonWidget* ribbon = getWidget<RibbonWidget>("options_choice");
|
||||||
assert(ribbon != NULL);
|
assert(ribbon != NULL);
|
||||||
ribbon->select( "tab_video", PLAYER_ID_GAME_MASTER );
|
ribbon->select( "tab_video", PLAYER_ID_GAME_MASTER );
|
||||||
@ -341,7 +412,7 @@ void OptionsScreenVideo::updateGfxSlider()
|
|||||||
{
|
{
|
||||||
if (m_presets[l].animatedCharacters == UserConfigParams::m_show_steering_animations &&
|
if (m_presets[l].animatedCharacters == UserConfigParams::m_show_steering_animations &&
|
||||||
m_presets[l].animatedScenery == UserConfigParams::m_graphical_effects &&
|
m_presets[l].animatedScenery == UserConfigParams::m_graphical_effects &&
|
||||||
m_presets[l].anisotropy == UserConfigParams::m_anisotropic &&
|
m_presets[l].image_quality == getImageQuality() &&
|
||||||
m_presets[l].bloom == UserConfigParams::m_bloom &&
|
m_presets[l].bloom == UserConfigParams::m_bloom &&
|
||||||
m_presets[l].glow == UserConfigParams::m_glow &&
|
m_presets[l].glow == UserConfigParams::m_glow &&
|
||||||
m_presets[l].lights == UserConfigParams::m_dynamic_lights &&
|
m_presets[l].lights == UserConfigParams::m_dynamic_lights &&
|
||||||
@ -354,8 +425,7 @@ void OptionsScreenVideo::updateGfxSlider()
|
|||||||
m_presets[l].weather == UserConfigParams::m_weather_effects &&
|
m_presets[l].weather == UserConfigParams::m_weather_effects &&
|
||||||
m_presets[l].dof == UserConfigParams::m_dof &&
|
m_presets[l].dof == UserConfigParams::m_dof &&
|
||||||
m_presets[l].global_illumination == UserConfigParams::m_gi &&
|
m_presets[l].global_illumination == UserConfigParams::m_gi &&
|
||||||
m_presets[l].degraded_ibl == UserConfigParams::m_degraded_IBL &&
|
m_presets[l].degraded_ibl == UserConfigParams::m_degraded_IBL)
|
||||||
m_presets[l].hd_textures == (UserConfigParams::m_high_definition_textures & 0x01))
|
|
||||||
{
|
{
|
||||||
gfx->setValue(l + 1);
|
gfx->setValue(l + 1);
|
||||||
found = true;
|
found = true;
|
||||||
@ -395,6 +465,19 @@ void OptionsScreenVideo::updateTooltip()
|
|||||||
//I18N: if no kart animations are enabled
|
//I18N: if no kart animations are enabled
|
||||||
const core::stringw none = _LTR("None");
|
const core::stringw none = _LTR("None");
|
||||||
|
|
||||||
|
//I18N: in the graphical options tooltip;
|
||||||
|
// indicates the rendered image quality is very low
|
||||||
|
const core::stringw very_low = _LTR("Very Low");
|
||||||
|
//I18N: in the graphical options tooltip;
|
||||||
|
// indicates the rendered image quality is low
|
||||||
|
const core::stringw low = _LTR("Low");
|
||||||
|
//I18N: in the graphical options tooltip;
|
||||||
|
// indicates the rendered image quality is high
|
||||||
|
const core::stringw high = _LTR("High");
|
||||||
|
//I18N: in the graphical options tooltip;
|
||||||
|
// indicates the rendered image quality is very high
|
||||||
|
const core::stringw very_high = _LTR("Very High");
|
||||||
|
|
||||||
//I18N: in graphical options
|
//I18N: in graphical options
|
||||||
// tooltip = tooltip + L"\n" + _("Pixel shaders: %s",
|
// tooltip = tooltip + L"\n" + _("Pixel shaders: %s",
|
||||||
// UserConfigParams::m_pixel_shaders ? enabled : disabled);
|
// UserConfigParams::m_pixel_shaders ? enabled : disabled);
|
||||||
@ -448,9 +531,11 @@ void OptionsScreenVideo::updateTooltip()
|
|||||||
UserConfigParams::m_gi ? enabled : disabled);
|
UserConfigParams::m_gi ? enabled : disabled);
|
||||||
|
|
||||||
//I18N: in graphical options
|
//I18N: in graphical options
|
||||||
tooltip = tooltip + L"\n" + _("Use high definition textures: %s",
|
int quality = getImageQuality();
|
||||||
(UserConfigParams::m_high_definition_textures & 0x1) == 0 ? disabled : enabled);
|
tooltip = tooltip + L"\n" + _("Rendered image quality: %s",
|
||||||
|
quality == 0 ? very_low : quality == 1 ? low : quality == 2 ?
|
||||||
|
high : very_high);
|
||||||
|
|
||||||
gfx->setTooltip(tooltip);
|
gfx->setTooltip(tooltip);
|
||||||
} // updateTooltip
|
} // updateTooltip
|
||||||
|
|
||||||
@ -518,7 +603,7 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name,
|
|||||||
|
|
||||||
UserConfigParams::m_show_steering_animations = m_presets[level].animatedCharacters;
|
UserConfigParams::m_show_steering_animations = m_presets[level].animatedCharacters;
|
||||||
UserConfigParams::m_graphical_effects = m_presets[level].animatedScenery;
|
UserConfigParams::m_graphical_effects = m_presets[level].animatedScenery;
|
||||||
UserConfigParams::m_anisotropic = m_presets[level].anisotropy;
|
setImageQuality(m_presets[level].image_quality);
|
||||||
UserConfigParams::m_bloom = m_presets[level].bloom;
|
UserConfigParams::m_bloom = m_presets[level].bloom;
|
||||||
UserConfigParams::m_glow = m_presets[level].glow;
|
UserConfigParams::m_glow = m_presets[level].glow;
|
||||||
UserConfigParams::m_dynamic_lights = m_presets[level].lights;
|
UserConfigParams::m_dynamic_lights = m_presets[level].lights;
|
||||||
@ -532,7 +617,6 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name,
|
|||||||
UserConfigParams::m_dof = m_presets[level].dof;
|
UserConfigParams::m_dof = m_presets[level].dof;
|
||||||
UserConfigParams::m_gi = m_presets[level].global_illumination;
|
UserConfigParams::m_gi = m_presets[level].global_illumination;
|
||||||
UserConfigParams::m_degraded_IBL = m_presets[level].degraded_ibl;
|
UserConfigParams::m_degraded_IBL = m_presets[level].degraded_ibl;
|
||||||
UserConfigParams::m_high_definition_textures = 0x02 | m_presets[level].hd_textures;
|
|
||||||
|
|
||||||
updateGfxSlider();
|
updateGfxSlider();
|
||||||
}
|
}
|
||||||
@ -563,6 +647,12 @@ void OptionsScreenVideo::tearDown()
|
|||||||
{
|
{
|
||||||
if (m_prev_adv_pipline != UserConfigParams::m_dynamic_lights)
|
if (m_prev_adv_pipline != UserConfigParams::m_dynamic_lights)
|
||||||
irr_driver->sameRestart();
|
irr_driver->sameRestart();
|
||||||
|
else if (m_prev_img_quality != getImageQuality())
|
||||||
|
{
|
||||||
|
irr_driver->setMaxTextureSize();
|
||||||
|
STKTexManager::getInstance()->destroyThreadedTexLoaders();
|
||||||
|
STKTexManager::getInstance()->createThreadedTexLoaders();
|
||||||
|
}
|
||||||
Screen::tearDown();
|
Screen::tearDown();
|
||||||
// save changes when leaving screen
|
// save changes when leaving screen
|
||||||
user_config->saveConfig();
|
user_config->saveConfig();
|
||||||
|
@ -38,12 +38,11 @@ struct GFXPreset
|
|||||||
bool weather;
|
bool weather;
|
||||||
bool animatedScenery;
|
bool animatedScenery;
|
||||||
int animatedCharacters;
|
int animatedCharacters;
|
||||||
int anisotropy;
|
int image_quality;
|
||||||
/** Depth of field */
|
/** Depth of field */
|
||||||
bool dof;
|
bool dof;
|
||||||
bool global_illumination;
|
bool global_illumination;
|
||||||
bool degraded_ibl;
|
bool degraded_ibl;
|
||||||
int hd_textures;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,6 +53,7 @@ class OptionsScreenVideo : public GUIEngine::Screen, public GUIEngine::ScreenSin
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
bool m_prev_adv_pipline;
|
bool m_prev_adv_pipline;
|
||||||
|
int m_prev_img_quality;
|
||||||
OptionsScreenVideo();
|
OptionsScreenVideo();
|
||||||
bool m_inited;
|
bool m_inited;
|
||||||
std::vector<GFXPreset> m_presets;
|
std::vector<GFXPreset> m_presets;
|
||||||
@ -81,6 +81,8 @@ public:
|
|||||||
virtual void unloaded() OVERRIDE;
|
virtual void unloaded() OVERRIDE;
|
||||||
|
|
||||||
void updateGfxSlider();
|
void updateGfxSlider();
|
||||||
|
static int getImageQuality();
|
||||||
|
static void setImageQuality(int quality);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -128,8 +128,11 @@ RaceGUI::RaceGUI()
|
|||||||
|
|
||||||
m_is_tutorial = (race_manager->getTrackName() == "tutorial");
|
m_is_tutorial = (race_manager->getTrackName() == "tutorial");
|
||||||
|
|
||||||
|
// Load speedmeter texture before rendering the first frame
|
||||||
m_speed_meter_icon = material_manager->getMaterial("speedback.png");
|
m_speed_meter_icon = material_manager->getMaterial("speedback.png");
|
||||||
|
m_speed_meter_icon->getTexture();
|
||||||
m_speed_bar_icon = material_manager->getMaterial("speedfore.png");
|
m_speed_bar_icon = material_manager->getMaterial("speedfore.png");
|
||||||
|
m_speed_bar_icon->getTexture();
|
||||||
//createMarkerTexture();
|
//createMarkerTexture();
|
||||||
} // RaceGUI
|
} // RaceGUI
|
||||||
|
|
||||||
@ -401,8 +404,7 @@ void RaceGUI::drawGlobalMiniMap()
|
|||||||
const Vec3& xyz = kart->getXYZ();
|
const Vec3& xyz = kart->getXYZ();
|
||||||
Vec3 draw_at;
|
Vec3 draw_at;
|
||||||
track->mapPoint2MiniMap(xyz, &draw_at);
|
track->mapPoint2MiniMap(xyz, &draw_at);
|
||||||
draw_at *= UserConfigParams::m_scale_rtts_factor;
|
|
||||||
|
|
||||||
video::ITexture* icon = sta ?
|
video::ITexture* icon = sta ?
|
||||||
irr_driver->getTexture(FileManager::GUI, "heart.png") :
|
irr_driver->getTexture(FileManager::GUI, "heart.png") :
|
||||||
kart->getKartProperties()->getMinimapIcon();
|
kart->getKartProperties()->getMinimapIcon();
|
||||||
@ -424,7 +426,7 @@ void RaceGUI::drawGlobalMiniMap()
|
|||||||
{
|
{
|
||||||
Vec3 draw_at;
|
Vec3 draw_at;
|
||||||
track->mapPoint2MiniMap(sw->getBallPosition(), &draw_at);
|
track->mapPoint2MiniMap(sw->getBallPosition(), &draw_at);
|
||||||
draw_at *= UserConfigParams::m_scale_rtts_factor;
|
|
||||||
video::ITexture* icon =
|
video::ITexture* icon =
|
||||||
irr_driver->getTexture(FileManager::GUI, "soccer_ball_normal.png");
|
irr_driver->getTexture(FileManager::GUI, "soccer_ball_normal.png");
|
||||||
|
|
||||||
|
@ -395,8 +395,7 @@ void RaceGUIOverworld::drawGlobalMiniMap()
|
|||||||
kart_xyz= kart->getXYZ();
|
kart_xyz= kart->getXYZ();
|
||||||
Vec3 draw_at;
|
Vec3 draw_at;
|
||||||
track->mapPoint2MiniMap(kart_xyz, &draw_at);
|
track->mapPoint2MiniMap(kart_xyz, &draw_at);
|
||||||
draw_at *= UserConfigParams::m_scale_rtts_factor;
|
|
||||||
|
|
||||||
video::ITexture* icon = kart->getKartProperties()->getMinimapIcon();
|
video::ITexture* icon = kart->getKartProperties()->getMinimapIcon();
|
||||||
core::rect<s32> source(core::position2di(0, 0), icon->getSize());
|
core::rect<s32> source(core::position2di(0, 0), icon->getSize());
|
||||||
int marker_half_size = (kart->getController()->isLocalPlayerController()
|
int marker_half_size = (kart->getController()->isLocalPlayerController()
|
||||||
@ -433,8 +432,7 @@ void RaceGUIOverworld::drawGlobalMiniMap()
|
|||||||
|
|
||||||
Vec3 draw_at;
|
Vec3 draw_at;
|
||||||
track->mapPoint2MiniMap(challenges[n].m_position, &draw_at);
|
track->mapPoint2MiniMap(challenges[n].m_position, &draw_at);
|
||||||
draw_at *= UserConfigParams::m_scale_rtts_factor;
|
|
||||||
|
|
||||||
const ChallengeData* challenge = unlock_manager->getChallengeData(challenges[n].m_challenge_id);
|
const ChallengeData* challenge = unlock_manager->getChallengeData(challenges[n].m_challenge_id);
|
||||||
const unsigned int val = challenge->getNumTrophies();
|
const unsigned int val = challenge->getNumTrophies();
|
||||||
bool unlocked = (PlayerManager::getCurrentPlayer()->getPoints() >= val);
|
bool unlocked = (PlayerManager::getCurrentPlayer()->getPoints() >= val);
|
||||||
|
@ -2178,9 +2178,7 @@ void Track::handleSky(const XMLNode &xml_node, const std::string &filename)
|
|||||||
if (CVS->isGLSL())
|
if (CVS->isGLSL())
|
||||||
{
|
{
|
||||||
t = STKTexManager::getInstance()->getTexture(v[i],
|
t = STKTexManager::getInstance()->getTexture(v[i],
|
||||||
false/*srgb*/, false/*premul_alpha*/,
|
(TexConfig*)NULL/*tex_config*/, true/*no_upload*/);
|
||||||
false/*set_material*/, false/*mesh_tex*/,
|
|
||||||
true/*no_upload*/);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif // !SERVER_ONLY
|
#endif // !SERVER_ONLY
|
||||||
@ -2224,9 +2222,7 @@ void Track::handleSky(const XMLNode &xml_node, const std::string &filename)
|
|||||||
if (CVS->isGLSL())
|
if (CVS->isGLSL())
|
||||||
{
|
{
|
||||||
t = STKTexManager::getInstance()->getTexture(v[i],
|
t = STKTexManager::getInstance()->getTexture(v[i],
|
||||||
false/*srgb*/, false/*premul_alpha*/,
|
(TexConfig*)NULL/*tex_config*/, true/*no_upload*/);
|
||||||
false/*set_material*/, false/*mesh_tex*/,
|
|
||||||
true/*no_upload*/);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
#endif // !SERVER_ONLY
|
#endif // !SERVER_ONLY
|
||||||
|
@ -862,9 +862,9 @@ TrackObjectPresentationBillboard::TrackObjectPresentationBillboard(
|
|||||||
xml_node.get("start", &m_fade_out_start);
|
xml_node.get("start", &m_fade_out_start);
|
||||||
xml_node.get("end", &m_fade_out_end );
|
xml_node.get("end", &m_fade_out_end );
|
||||||
}
|
}
|
||||||
|
TexConfig tc(true/*srgb*/, true/*premul_alpha*/);
|
||||||
video::ITexture* texture = STKTexManager::getInstance()->getTexture
|
video::ITexture* texture = STKTexManager::getInstance()->getTexture
|
||||||
(file_manager->searchTexture(texture_name), true/*srgb*/,
|
(file_manager->searchTexture(texture_name), &tc);
|
||||||
true/*premul_alpha*/, false/*set_material*/, true/*mesh_tex*/);
|
|
||||||
|
|
||||||
if (texture == NULL)
|
if (texture == NULL)
|
||||||
{
|
{
|
||||||
|
625
src/utils/avi_writer.cpp
Normal file
625
src/utils/avi_writer.cpp
Normal file
@ -0,0 +1,625 @@
|
|||||||
|
//
|
||||||
|
// SuperTuxKart - a fun racing game with go-kart
|
||||||
|
// Copyright (C) 2015 Dawid Gan
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 3
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
|
||||||
|
#include "utils/avi_writer.hpp"
|
||||||
|
#include "config/user_config.hpp"
|
||||||
|
#include "graphics/irr_driver.hpp"
|
||||||
|
#include "guiengine/message_queue.hpp"
|
||||||
|
#include "utils/translation.hpp"
|
||||||
|
#include "utils/vs.hpp"
|
||||||
|
|
||||||
|
#include <jpeglib.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
Synchronised<std::string> AVIWriter::m_recording_target("");
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
AVIWriter::AVIWriter() : m_idle(true)
|
||||||
|
{
|
||||||
|
resetFrameBufferImage();
|
||||||
|
resetCaptureFormat();
|
||||||
|
m_file = NULL;
|
||||||
|
m_last_junk_chunk = 0;
|
||||||
|
m_end_of_header = 0;
|
||||||
|
m_movi_start = 0;
|
||||||
|
m_stream_bytes = 0;
|
||||||
|
m_total_frames = 0;
|
||||||
|
m_chunk_fcc = 0;
|
||||||
|
m_width = irr_driver->getActualScreenSize().Width;
|
||||||
|
m_height = irr_driver->getActualScreenSize().Height;
|
||||||
|
glGenBuffers(3, m_pbo);
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[i]);
|
||||||
|
glBufferData(GL_PIXEL_PACK_BUFFER, m_width * m_height * 4, NULL,
|
||||||
|
GL_STREAM_READ);
|
||||||
|
}
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
pthread_cond_init(&m_cond_request, NULL);
|
||||||
|
pthread_create(&m_thread, NULL, &startRoutine, this);
|
||||||
|
} // AVIWriter
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
AVIWriter::~AVIWriter()
|
||||||
|
{
|
||||||
|
glDeleteBuffers(3, m_pbo);
|
||||||
|
addFrameBufferImage(NULL, 0);
|
||||||
|
if (!waitForReadyToDeleted(2.0f))
|
||||||
|
Log::info("AVIWriter", "AVIWriter not stopping, exiting anyway.");
|
||||||
|
pthread_join(m_thread, NULL);
|
||||||
|
pthread_cond_destroy(&m_cond_request);
|
||||||
|
} // ~AVIWriter
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void AVIWriter::resetFrameBufferImage()
|
||||||
|
{
|
||||||
|
m_pbo_use = 0;
|
||||||
|
m_accumulated_time = 0.0f;
|
||||||
|
m_remaining_time = 0.0f;
|
||||||
|
} // resetFrameBufferImage
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void AVIWriter::resetCaptureFormat()
|
||||||
|
{
|
||||||
|
m_img_quality = UserConfigParams::m_record_compression;
|
||||||
|
m_msec_per_frame = unsigned(1000 / UserConfigParams::m_record_fps);
|
||||||
|
m_avi_format =
|
||||||
|
UserConfigParams::m_record_bmp ? AVI_FORMAT_BMP : AVI_FORMAT_JPG;
|
||||||
|
} // resetCaptureFormat
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void* AVIWriter::startRoutine(void *obj)
|
||||||
|
{
|
||||||
|
VS::setThreadName("AVIWriter");
|
||||||
|
AVIWriter* avi_writer = (AVIWriter*)obj;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
avi_writer->m_fbi_queue.lock();
|
||||||
|
bool waiting = avi_writer->m_fbi_queue.getData().empty();
|
||||||
|
while (waiting)
|
||||||
|
{
|
||||||
|
pthread_cond_wait(&avi_writer->m_cond_request,
|
||||||
|
avi_writer->m_fbi_queue.getMutex());
|
||||||
|
waiting = avi_writer->m_fbi_queue.getData().empty();
|
||||||
|
}
|
||||||
|
auto& p = avi_writer->m_fbi_queue.getData().front();
|
||||||
|
uint8_t* fbi = p.first;
|
||||||
|
int frame_count = p.second;
|
||||||
|
if (frame_count == -1)
|
||||||
|
{
|
||||||
|
avi_writer->closeFile();
|
||||||
|
avi_writer->m_idle.setAtomic(true);
|
||||||
|
avi_writer->m_fbi_queue.getData().pop_front();
|
||||||
|
avi_writer->m_fbi_queue.unlock();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (fbi == NULL)
|
||||||
|
{
|
||||||
|
avi_writer->closeFile(false/*delete_file*/, true/*exiting*/);
|
||||||
|
avi_writer->setCanBeDeleted();
|
||||||
|
avi_writer->m_fbi_queue.getData().pop_front();
|
||||||
|
avi_writer->m_fbi_queue.unlock();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
const bool too_slow = avi_writer->m_fbi_queue.getData().size() > 50;
|
||||||
|
avi_writer->m_fbi_queue.getData().pop_front();
|
||||||
|
avi_writer->m_fbi_queue.unlock();
|
||||||
|
if (too_slow)
|
||||||
|
{
|
||||||
|
MessageQueue::add(MessageQueue::MT_ERROR,
|
||||||
|
_("Encoding is too slow, dropping frames."));
|
||||||
|
delete [] fbi;
|
||||||
|
avi_writer->cleanAllFrameBufferImages();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (avi_writer->m_file == NULL)
|
||||||
|
{
|
||||||
|
bool ret = avi_writer->createFile();
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
delete [] fbi;
|
||||||
|
avi_writer->cleanAllFrameBufferImages();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint8_t* orig_fbi = fbi;
|
||||||
|
const unsigned width = avi_writer->m_width;
|
||||||
|
const unsigned height = avi_writer->m_height;
|
||||||
|
const unsigned area = width * height;
|
||||||
|
int size = area * 4;
|
||||||
|
int dest = size - 3;
|
||||||
|
int src = size - 4;
|
||||||
|
int copied = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (copied++ > 1)
|
||||||
|
memcpy(fbi + dest, fbi + src, 3);
|
||||||
|
else
|
||||||
|
memmove(fbi + dest, fbi + src, 3);
|
||||||
|
if (src == 0)
|
||||||
|
break;
|
||||||
|
dest -= 3;
|
||||||
|
src -= 4;
|
||||||
|
}
|
||||||
|
fbi = fbi + area;
|
||||||
|
const int pitch = width * 3;
|
||||||
|
uint8_t* p2 = fbi + (height - 1) * pitch;
|
||||||
|
uint8_t* tmp_buf = new uint8_t[pitch];
|
||||||
|
for (unsigned i = 0; i < height; i += 2)
|
||||||
|
{
|
||||||
|
memcpy(tmp_buf, fbi, pitch);
|
||||||
|
memcpy(fbi, p2, pitch);
|
||||||
|
memcpy(p2, tmp_buf, pitch);
|
||||||
|
fbi += pitch;
|
||||||
|
p2 -= pitch;
|
||||||
|
}
|
||||||
|
delete [] tmp_buf;
|
||||||
|
size = area * 3;
|
||||||
|
if (avi_writer->m_avi_format == AVI_FORMAT_JPG)
|
||||||
|
{
|
||||||
|
uint8_t* jpg = new uint8_t[size];
|
||||||
|
size = avi_writer->bmpToJpg(orig_fbi + area, jpg, size);
|
||||||
|
delete [] orig_fbi;
|
||||||
|
orig_fbi = jpg;
|
||||||
|
}
|
||||||
|
while (frame_count != 0)
|
||||||
|
{
|
||||||
|
AVIErrCode code = avi_writer->addImage
|
||||||
|
(avi_writer->m_avi_format == AVI_FORMAT_JPG ? orig_fbi :
|
||||||
|
orig_fbi + area, size);
|
||||||
|
if (code == AVI_SIZE_LIMIT_ERR)
|
||||||
|
{
|
||||||
|
avi_writer->createFile();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (code == AVI_IO_ERR)
|
||||||
|
break;
|
||||||
|
frame_count--;
|
||||||
|
}
|
||||||
|
delete [] orig_fbi;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
} // startRoutine
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
int AVIWriter::getFrameCount(float dt)
|
||||||
|
{
|
||||||
|
const float frame_rate = 0.001f * m_msec_per_frame;
|
||||||
|
m_accumulated_time += dt;
|
||||||
|
if (m_accumulated_time < frame_rate && m_remaining_time < frame_rate)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int frame_count = 1;
|
||||||
|
m_remaining_time += m_accumulated_time - frame_rate;
|
||||||
|
m_accumulated_time = 0.0f;
|
||||||
|
while (m_remaining_time > frame_rate)
|
||||||
|
{
|
||||||
|
frame_count++;
|
||||||
|
m_remaining_time -= frame_rate;
|
||||||
|
}
|
||||||
|
return frame_count;
|
||||||
|
} // getFrameCount
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
void AVIWriter::captureFrameBufferImage(float dt)
|
||||||
|
{
|
||||||
|
glReadBuffer(GL_BACK);
|
||||||
|
int pbo_read = -1;
|
||||||
|
if (m_pbo_use > 3 && m_pbo_use % 3 == 0)
|
||||||
|
m_pbo_use = 3;
|
||||||
|
if (m_pbo_use >= 3)
|
||||||
|
{
|
||||||
|
int frame_count = getFrameCount(dt);
|
||||||
|
if (frame_count != 0)
|
||||||
|
{
|
||||||
|
pbo_read = m_pbo_use % 3;
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_read]);
|
||||||
|
void* ptr = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
|
||||||
|
const unsigned size = m_width * m_height * 4;
|
||||||
|
uint8_t* fbi = new uint8_t[size];
|
||||||
|
memcpy(fbi, ptr, size);
|
||||||
|
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
|
||||||
|
addFrameBufferImage(fbi, frame_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int pbo_use = m_pbo_use++ % 3;
|
||||||
|
assert(pbo_read == -1 || pbo_use == pbo_read);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, m_pbo[pbo_use]);
|
||||||
|
glReadPixels(0, 0, m_width, m_height,
|
||||||
|
m_avi_format == AVI_FORMAT_JPG ? GL_RGBA: GL_BGRA,
|
||||||
|
GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
|
||||||
|
} // captureFrameBufferImage
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
bool AVIWriter::addJUNKChunk(std::string str, unsigned int min_size)
|
||||||
|
{
|
||||||
|
int size = str.size() < min_size ? min_size : str.size() + 1;
|
||||||
|
size = (size + 1) & 0xfffffffe;
|
||||||
|
|
||||||
|
CHUNK chunk;
|
||||||
|
chunk.fcc = FOURCC('J', 'U', 'N', 'K');
|
||||||
|
chunk.cb = size;
|
||||||
|
|
||||||
|
char* buffer = (char*)calloc(size, 1);
|
||||||
|
strcpy(buffer, str.c_str());
|
||||||
|
|
||||||
|
int num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||||
|
if (num != sizeof(chunk))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
num = fwrite(buffer, 1, size * sizeof(char), m_file);
|
||||||
|
free(buffer);
|
||||||
|
if (num != size)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
m_last_junk_chunk = ftell(m_file);
|
||||||
|
if (m_last_junk_chunk < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
closeFile(true/*delete_file*/);
|
||||||
|
return false;
|
||||||
|
} // addJUNKChunk
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
AVIErrCode AVIWriter::addImage(unsigned char* buffer, int buf_size)
|
||||||
|
{
|
||||||
|
if (m_file == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
int num; num = ftell(m_file);
|
||||||
|
if (num < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (m_total_frames >= MAX_FRAMES)
|
||||||
|
goto size_limit;
|
||||||
|
|
||||||
|
CHUNK chunk;
|
||||||
|
chunk.fcc = m_chunk_fcc;
|
||||||
|
chunk.cb = buf_size;
|
||||||
|
|
||||||
|
m_index_table[m_total_frames].Offset = num;
|
||||||
|
m_index_table[m_total_frames].Length = chunk.cb;
|
||||||
|
m_index_table[m_total_frames].fcc = chunk.fcc;
|
||||||
|
|
||||||
|
num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||||
|
if (num != sizeof(chunk))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
num = fwrite(buffer, 1, buf_size, m_file);
|
||||||
|
if (num != buf_size)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
int fill_size; fill_size = (sizeof(chunk) + buf_size) & 0x00000001;
|
||||||
|
if (fill_size > 0)
|
||||||
|
{
|
||||||
|
uint32_t filler = 0;
|
||||||
|
num = fwrite(&filler, 1, fill_size, m_file);
|
||||||
|
if (num != fill_size)
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stream_bytes += sizeof(chunk) + buf_size + fill_size;
|
||||||
|
m_total_frames++;
|
||||||
|
|
||||||
|
num = ftell(m_file);
|
||||||
|
if (num < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (((num - m_last_junk_chunk) > 20000) && (!addJUNKChunk("", 1)))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
// check if we reached the file size limit
|
||||||
|
if (num >= MAX_FILE_SIZE)
|
||||||
|
goto size_limit;
|
||||||
|
|
||||||
|
return AVI_SUCCESS;
|
||||||
|
|
||||||
|
error:
|
||||||
|
closeFile(true/*delete_file*/);
|
||||||
|
return AVI_IO_ERR;
|
||||||
|
|
||||||
|
size_limit:
|
||||||
|
MessageQueue::add(MessageQueue::MT_GENERIC,
|
||||||
|
_("Video exceeded size limit, starting a new one."));
|
||||||
|
closeFile();
|
||||||
|
return AVI_SIZE_LIMIT_ERR;
|
||||||
|
} // addImage
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
bool AVIWriter::closeFile(bool delete_file, bool exiting)
|
||||||
|
{
|
||||||
|
if (m_file == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (delete_file)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
// add the index
|
||||||
|
int idx_start; idx_start = ftell(m_file);
|
||||||
|
if (idx_start < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
CHUNK chunk;
|
||||||
|
chunk.fcc = FOURCC('i', 'd', 'x', '1');
|
||||||
|
chunk.cb = sizeof(AVIINDEXENTRY) * m_total_frames;
|
||||||
|
|
||||||
|
int num; num = fwrite(&chunk, 1, sizeof(chunk), m_file);
|
||||||
|
if (num != sizeof(chunk))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < m_total_frames; i++)
|
||||||
|
{
|
||||||
|
AVIINDEXENTRY Index;
|
||||||
|
Index.ckid = m_index_table[i].fcc;
|
||||||
|
Index.dwFlags = AVIIF_KEYFRAME;
|
||||||
|
Index.dwChunkOffset = m_index_table[i].Offset;
|
||||||
|
Index.dwChunkLength = m_index_table[i].Length;
|
||||||
|
|
||||||
|
num = fwrite(&Index, 1, sizeof(Index), m_file);
|
||||||
|
if (num != sizeof(Index))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the header
|
||||||
|
if (m_total_frames > 0 && m_msec_per_frame > 0)
|
||||||
|
{
|
||||||
|
num = fseek(m_file, 0, SEEK_END);
|
||||||
|
if (num < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
int size; size = ftell(m_file);
|
||||||
|
if (size < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
num = fseek(m_file, 0, SEEK_SET);
|
||||||
|
if (num < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
m_avi_hdr.riff.cb = size - sizeof(m_avi_hdr.riff);
|
||||||
|
m_avi_hdr.avih.dwMaxBytesPerSec = (uint32_t)
|
||||||
|
(((m_stream_bytes / m_total_frames) * m_format_hdr.strh.dwRate) /
|
||||||
|
m_msec_per_frame + 0.5f);
|
||||||
|
m_avi_hdr.avih.dwTotalFrames = m_total_frames;
|
||||||
|
|
||||||
|
num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
|
||||||
|
if (num != sizeof(m_avi_hdr))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
m_format_hdr.strh.dwLength = m_total_frames;
|
||||||
|
|
||||||
|
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
|
||||||
|
if (num != sizeof(m_format_hdr))
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the movi section
|
||||||
|
m_movi_chunk.cb = idx_start - m_movi_start;
|
||||||
|
|
||||||
|
num = fseek(m_file, m_movi_start - sizeof(m_movi_chunk), SEEK_SET);
|
||||||
|
if (num < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
|
||||||
|
if (num != sizeof(m_movi_chunk))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
fclose(m_file);
|
||||||
|
m_file = NULL;
|
||||||
|
|
||||||
|
if (!exiting)
|
||||||
|
{
|
||||||
|
MessageQueue::add(MessageQueue::MT_GENERIC,
|
||||||
|
_("Video saved in \"%s\".", m_filename.c_str()));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (!exiting)
|
||||||
|
{
|
||||||
|
MessageQueue::add(MessageQueue::MT_ERROR,
|
||||||
|
_("Error when saving video."));
|
||||||
|
}
|
||||||
|
fclose(m_file);
|
||||||
|
remove(m_filename.c_str());
|
||||||
|
m_file = NULL;
|
||||||
|
return false;
|
||||||
|
} // closeFile
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
bool AVIWriter::createFile()
|
||||||
|
{
|
||||||
|
m_idle.setAtomic(false);
|
||||||
|
time_t rawtime;
|
||||||
|
time(&rawtime);
|
||||||
|
tm* timeInfo = localtime(&rawtime);
|
||||||
|
char time_buffer[256];
|
||||||
|
sprintf(time_buffer, "%i.%02i.%02i_%02i.%02i.%02i",
|
||||||
|
timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
|
||||||
|
timeInfo->tm_mday, timeInfo->tm_hour,
|
||||||
|
timeInfo->tm_min, timeInfo->tm_sec);
|
||||||
|
|
||||||
|
m_filename = m_recording_target.getAtomic() + "-" + time_buffer + ".avi";
|
||||||
|
m_stream_bytes = 0;
|
||||||
|
m_total_frames = 0;
|
||||||
|
m_movi_start = 0;
|
||||||
|
m_last_junk_chunk = 0;
|
||||||
|
|
||||||
|
BitmapInfoHeader bitmap_hdr;
|
||||||
|
bitmap_hdr.biSize = sizeof(BitmapInfoHeader);
|
||||||
|
bitmap_hdr.biWidth = m_width;
|
||||||
|
bitmap_hdr.biHeight = m_height;
|
||||||
|
bitmap_hdr.biPlanes = 1;
|
||||||
|
bitmap_hdr.biBitCount = 24;
|
||||||
|
bitmap_hdr.biCompression = 0;
|
||||||
|
bitmap_hdr.biSizeImage = (m_width * m_height * 3 * bitmap_hdr.biPlanes);
|
||||||
|
bitmap_hdr.biXPelsPerMeter = 0;
|
||||||
|
bitmap_hdr.biYPelsPerMeter = 0;
|
||||||
|
bitmap_hdr.biClrUsed = 0;
|
||||||
|
bitmap_hdr.biClrImportant = 0;
|
||||||
|
|
||||||
|
memset(&m_avi_hdr, '\0', sizeof(m_avi_hdr));
|
||||||
|
m_avi_hdr.riff.fcc = FOURCC('R', 'I', 'F', 'F');
|
||||||
|
m_avi_hdr.riff.cb = 0; // update when finished (size of the file - 8)
|
||||||
|
m_avi_hdr.avi = FOURCC('A', 'V', 'I', ' ');
|
||||||
|
m_avi_hdr.list1.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||||
|
m_avi_hdr.list1.cb = 0;
|
||||||
|
m_avi_hdr.hdrl = FOURCC('h', 'd', 'r', 'l');
|
||||||
|
m_avi_hdr.avihhdr.fcc = FOURCC('a', 'v', 'i', 'h');
|
||||||
|
m_avi_hdr.avihhdr.cb = sizeof(m_avi_hdr.avih);
|
||||||
|
m_avi_hdr.avih.dwMicroSecPerFrame = m_msec_per_frame * 1000;
|
||||||
|
m_avi_hdr.avih.dwMaxBytesPerSec = 0; // update when finished
|
||||||
|
m_avi_hdr.avih.dwPaddingGranularity = 0;
|
||||||
|
m_avi_hdr.avih.dwFlags = AVIF_WASCAPTUREFILE | AVIF_HASINDEX;
|
||||||
|
m_avi_hdr.avih.dwTotalFrames = 0; // update when finished
|
||||||
|
m_avi_hdr.avih.dwInitialFrames = 0;
|
||||||
|
m_avi_hdr.avih.dwStreams = 1; // 1 = video, 2 = video and audio
|
||||||
|
m_avi_hdr.avih.dwSuggestedBufferSize = 0; // can be just 0
|
||||||
|
m_avi_hdr.avih.dwWidth = m_width;
|
||||||
|
m_avi_hdr.avih.dwHeight = m_height;
|
||||||
|
|
||||||
|
m_format_hdr.list.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||||
|
m_format_hdr.list.cb = (sizeof(m_format_hdr) - 8) +
|
||||||
|
sizeof(BitmapInfoHeader);
|
||||||
|
m_format_hdr.strl = FOURCC('s', 't', 'r', 'l');
|
||||||
|
m_format_hdr.strhhdr.fcc = FOURCC('s', 't', 'r', 'h');
|
||||||
|
m_format_hdr.strhhdr.cb = sizeof(m_format_hdr.strh);
|
||||||
|
m_format_hdr.strh.fccType = FOURCC('v', 'i', 'd', 's');
|
||||||
|
m_format_hdr.strh.fccHandler = CC_DIB;
|
||||||
|
m_format_hdr.strh.dwFlags = 0;
|
||||||
|
m_format_hdr.strh.wPriority = 0;
|
||||||
|
m_format_hdr.strh.wLanguage = 0;
|
||||||
|
m_format_hdr.strh.dwInitialFrames = 0;
|
||||||
|
m_format_hdr.strh.dwScale = m_msec_per_frame;
|
||||||
|
m_format_hdr.strh.dwRate = 1000;
|
||||||
|
m_format_hdr.strh.dwStart = 0;
|
||||||
|
m_format_hdr.strh.dwLength = 0; // update when finished
|
||||||
|
m_format_hdr.strh.dwSuggestedBufferSize = 0; // can be just 0
|
||||||
|
m_format_hdr.strh.dwQuality = m_img_quality * 100;
|
||||||
|
m_format_hdr.strh.dwSampleSize = 0;
|
||||||
|
m_format_hdr.strh.Left = 0;
|
||||||
|
m_format_hdr.strh.Top = 0;
|
||||||
|
m_format_hdr.strh.Right = m_avi_hdr.avih.dwWidth;
|
||||||
|
m_format_hdr.strh.Bottom = m_avi_hdr.avih.dwHeight;
|
||||||
|
m_format_hdr.strfhdr.fcc = FOURCC('s', 't', 'r', 'f');
|
||||||
|
m_format_hdr.strfhdr.cb = sizeof(BitmapInfoHeader);
|
||||||
|
|
||||||
|
// Format specific changes
|
||||||
|
if (m_avi_format == AVI_FORMAT_JPG)
|
||||||
|
{
|
||||||
|
m_format_hdr.strh.fccHandler = CC_MJPG;
|
||||||
|
bitmap_hdr.biCompression = FOURCC('M', 'J', 'P', 'G');
|
||||||
|
m_chunk_fcc = FOURCC('0', '0', 'd', 'c');
|
||||||
|
}
|
||||||
|
else if (m_avi_format == AVI_FORMAT_BMP)
|
||||||
|
{
|
||||||
|
bitmap_hdr.biHeight = -m_height;
|
||||||
|
bitmap_hdr.biCompression = 0;
|
||||||
|
m_chunk_fcc = FOURCC('0', '0', 'd', 'b');
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t fcc_movi = FOURCC('m', 'o', 'v', 'i');
|
||||||
|
|
||||||
|
m_file = fopen(m_filename.c_str(), "wb");
|
||||||
|
if (m_file == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int num = fwrite(&m_avi_hdr, 1, sizeof(m_avi_hdr), m_file);
|
||||||
|
if (num != sizeof(m_avi_hdr))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
num = fwrite(&m_format_hdr, 1, sizeof(m_format_hdr), m_file);
|
||||||
|
if (num != sizeof(m_format_hdr))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
num = fwrite(&bitmap_hdr, 1, sizeof(BitmapInfoHeader), m_file);
|
||||||
|
if (num != sizeof(BitmapInfoHeader))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
m_end_of_header = ftell(m_file);
|
||||||
|
if (m_end_of_header < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!addJUNKChunk("", 2840))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
m_avi_hdr.list1.cb = m_end_of_header - sizeof(m_avi_hdr.riff) -
|
||||||
|
sizeof(m_avi_hdr.avi) - sizeof(m_avi_hdr.list1);
|
||||||
|
m_movi_chunk.fcc = FOURCC('L', 'I', 'S', 'T');
|
||||||
|
m_movi_chunk.cb = 0; // update when finished
|
||||||
|
|
||||||
|
num = fwrite(&m_movi_chunk, 1, sizeof(m_movi_chunk), m_file);
|
||||||
|
if (num != sizeof(m_movi_chunk))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
m_movi_start = ftell(m_file);
|
||||||
|
if (m_movi_start < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
num = fwrite(&fcc_movi, 1, sizeof(fcc_movi), m_file);
|
||||||
|
if (num != sizeof(fcc_movi))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
closeFile(true/*delete_file*/);
|
||||||
|
return false;
|
||||||
|
} // createFile
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
int AVIWriter::bmpToJpg(unsigned char* image_data, unsigned char* image_output,
|
||||||
|
unsigned long buf_length)
|
||||||
|
{
|
||||||
|
struct jpeg_compress_struct cinfo;
|
||||||
|
struct jpeg_error_mgr jerr;
|
||||||
|
cinfo.err = jpeg_std_error(&jerr);
|
||||||
|
|
||||||
|
jpeg_create_compress(&cinfo);
|
||||||
|
|
||||||
|
cinfo.image_width = m_width;
|
||||||
|
cinfo.image_height = m_height;
|
||||||
|
cinfo.input_components = 3;
|
||||||
|
cinfo.in_color_space = JCS_RGB;
|
||||||
|
|
||||||
|
jpeg_set_defaults(&cinfo);
|
||||||
|
jpeg_set_quality(&cinfo, m_img_quality, true);
|
||||||
|
|
||||||
|
jpeg_mem_dest(&cinfo, &image_output, &buf_length);
|
||||||
|
|
||||||
|
jpeg_start_compress(&cinfo, true);
|
||||||
|
|
||||||
|
JSAMPROW jrow[1];
|
||||||
|
while (cinfo.next_scanline < cinfo.image_height)
|
||||||
|
{
|
||||||
|
jrow[0] = &image_data[cinfo.next_scanline * m_width * 3];
|
||||||
|
jpeg_write_scanlines(&cinfo, jrow, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
jpeg_finish_compress(&cinfo);
|
||||||
|
jpeg_destroy_compress(&cinfo);
|
||||||
|
|
||||||
|
return buf_length;
|
||||||
|
} // bmpToJpg
|
||||||
|
|
||||||
|
#endif
|
259
src/utils/avi_writer.hpp
Normal file
259
src/utils/avi_writer.hpp
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
//
|
||||||
|
// SuperTuxKart - a fun racing game with go-kart
|
||||||
|
// Copyright (C) 2015 Dawid Gan
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU General Public License
|
||||||
|
// as published by the Free Software Foundation; either version 3
|
||||||
|
// of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||||
|
|
||||||
|
#include "graphics/gl_headers.hpp"
|
||||||
|
#include "utils/can_be_deleted.hpp"
|
||||||
|
#include "utils/no_copy.hpp"
|
||||||
|
#include "utils/singleton.hpp"
|
||||||
|
#include "utils/synchronised.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#define FOURCC(a,b,c,d) ((uint32_t) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a)))
|
||||||
|
|
||||||
|
const uint32_t CC_MJPG = FOURCC('m', 'j', 'p', 'g');
|
||||||
|
const uint32_t CC_DIB = FOURCC('\0', '\0', '\0', '\0');
|
||||||
|
const uint32_t CC_VIDS = FOURCC('v', 'i', 'd', 's');
|
||||||
|
|
||||||
|
const uint32_t AVIF_HASINDEX = 0x00000010;
|
||||||
|
const uint32_t AVIF_MUSTUSEINDEX = 0x00000020;
|
||||||
|
const uint32_t AVIF_ISINTERLEAVED = 0x00000100;
|
||||||
|
const uint32_t AVIF_TRUSTCKTYPE = 0x00000800;
|
||||||
|
const uint32_t AVIF_WASCAPTUREFILE = 0x00010000;
|
||||||
|
const uint32_t AVIF_COPYRIGHTED = 0x00020000;
|
||||||
|
|
||||||
|
const uint32_t AVISF_DISABLED = 0x00000001;
|
||||||
|
const uint32_t AVISF_VIDEO_PALCHANGES = 0x00010000;
|
||||||
|
|
||||||
|
const uint32_t AVIIF_LIST = 0x00000001;
|
||||||
|
const uint32_t AVIIF_KEYFRAME = 0x00000010;
|
||||||
|
const uint32_t AVIIF_FIRSTPART = 0x00000020;
|
||||||
|
const uint32_t AVIIF_LASTPART = 0x00000040;
|
||||||
|
const uint32_t AVIIF_MIDPART = 0x00000060;
|
||||||
|
const uint32_t AVIIF_NOTIME = 0x00000100;
|
||||||
|
const uint32_t AVIIF_COMPUSE = 0x0FFF0000;
|
||||||
|
|
||||||
|
enum AVIFormat
|
||||||
|
{
|
||||||
|
AVI_FORMAT_BMP,
|
||||||
|
AVI_FORMAT_JPG
|
||||||
|
};
|
||||||
|
|
||||||
|
enum AVIErrCode
|
||||||
|
{
|
||||||
|
AVI_SUCCESS,
|
||||||
|
AVI_SIZE_LIMIT_ERR,
|
||||||
|
AVI_IO_ERR
|
||||||
|
};
|
||||||
|
|
||||||
|
const int MAX_FRAMES = 1000000;
|
||||||
|
const int MAX_FILE_SIZE = 2000000000;
|
||||||
|
|
||||||
|
struct MainAVIHeader
|
||||||
|
{
|
||||||
|
uint32_t dwMicroSecPerFrame;
|
||||||
|
uint32_t dwMaxBytesPerSec;
|
||||||
|
uint32_t dwPaddingGranularity;
|
||||||
|
uint32_t dwFlags;
|
||||||
|
uint32_t dwTotalFrames;
|
||||||
|
uint32_t dwInitialFrames;
|
||||||
|
uint32_t dwStreams;
|
||||||
|
uint32_t dwSuggestedBufferSize;
|
||||||
|
uint32_t dwWidth;
|
||||||
|
uint32_t dwHeight;
|
||||||
|
uint32_t dwReserved[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AVIStreamHeader
|
||||||
|
{
|
||||||
|
uint32_t fccType;
|
||||||
|
uint32_t fccHandler;
|
||||||
|
uint32_t dwFlags;
|
||||||
|
uint16_t wPriority;
|
||||||
|
uint16_t wLanguage;
|
||||||
|
uint32_t dwInitialFrames;
|
||||||
|
uint32_t dwScale;
|
||||||
|
uint32_t dwRate;
|
||||||
|
uint32_t dwStart;
|
||||||
|
uint32_t dwLength;
|
||||||
|
uint32_t dwSuggestedBufferSize;
|
||||||
|
uint32_t dwQuality;
|
||||||
|
uint32_t dwSampleSize;
|
||||||
|
uint16_t Left;
|
||||||
|
uint16_t Top;
|
||||||
|
uint16_t Right;
|
||||||
|
uint16_t Bottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BitmapInfoHeader
|
||||||
|
{
|
||||||
|
uint32_t biSize;
|
||||||
|
uint32_t biWidth;
|
||||||
|
uint32_t biHeight;
|
||||||
|
uint16_t biPlanes;
|
||||||
|
uint16_t biBitCount;
|
||||||
|
uint32_t biCompression;
|
||||||
|
uint32_t biSizeImage;
|
||||||
|
uint32_t biXPelsPerMeter;
|
||||||
|
uint32_t biYPelsPerMeter;
|
||||||
|
uint32_t biClrUsed;
|
||||||
|
uint32_t biClrImportant;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AVIINDEXENTRY
|
||||||
|
{
|
||||||
|
uint32_t ckid;
|
||||||
|
uint32_t dwFlags;
|
||||||
|
uint32_t dwChunkOffset;
|
||||||
|
uint32_t dwChunkLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CHUNK
|
||||||
|
{
|
||||||
|
uint32_t fcc;
|
||||||
|
uint32_t cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AVIHeader
|
||||||
|
{
|
||||||
|
CHUNK riff;
|
||||||
|
uint32_t avi;
|
||||||
|
CHUNK list1;
|
||||||
|
uint32_t hdrl;
|
||||||
|
CHUNK avihhdr;
|
||||||
|
MainAVIHeader avih;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FormatHeader
|
||||||
|
{
|
||||||
|
CHUNK list;
|
||||||
|
uint32_t strl;
|
||||||
|
CHUNK strhhdr;
|
||||||
|
AVIStreamHeader strh;
|
||||||
|
CHUNK strfhdr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IndexTable
|
||||||
|
{
|
||||||
|
uint32_t Offset;
|
||||||
|
uint32_t Length;
|
||||||
|
uint32_t fcc;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AVIWriter : public CanBeDeleted, public NoCopy,
|
||||||
|
public Singleton<AVIWriter>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
FILE* m_file;
|
||||||
|
|
||||||
|
static Synchronised<std::string> m_recording_target;
|
||||||
|
|
||||||
|
std::string m_filename;
|
||||||
|
|
||||||
|
int m_last_junk_chunk, m_end_of_header, m_movi_start, m_img_quality,
|
||||||
|
m_width, m_height;
|
||||||
|
|
||||||
|
unsigned int m_msec_per_frame, m_stream_bytes, m_total_frames, m_pbo_use;
|
||||||
|
|
||||||
|
float m_accumulated_time, m_remaining_time;
|
||||||
|
|
||||||
|
AVIFormat m_avi_format;
|
||||||
|
|
||||||
|
AVIHeader m_avi_hdr;
|
||||||
|
|
||||||
|
CHUNK m_movi_chunk;
|
||||||
|
|
||||||
|
FormatHeader m_format_hdr;
|
||||||
|
|
||||||
|
IndexTable m_index_table[MAX_FRAMES];
|
||||||
|
|
||||||
|
uint32_t m_chunk_fcc;
|
||||||
|
|
||||||
|
Synchronised<std::list<std::pair<uint8_t*, int> > > m_fbi_queue;
|
||||||
|
|
||||||
|
Synchronised<bool> m_idle;
|
||||||
|
|
||||||
|
pthread_t m_thread;
|
||||||
|
|
||||||
|
pthread_cond_t m_cond_request;
|
||||||
|
|
||||||
|
GLuint m_pbo[3];
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
int bmpToJpg(unsigned char* image_data, unsigned char* image_output,
|
||||||
|
unsigned long buf_length);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
AVIErrCode addImage(unsigned char* buffer, int size);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool closeFile(bool delete_file = false, bool exiting = false);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool createFile();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool addJUNKChunk(std::string str, unsigned int min_size);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void addFrameBufferImage(uint8_t* fbi, int frame_count)
|
||||||
|
{
|
||||||
|
m_fbi_queue.lock();
|
||||||
|
m_fbi_queue.getData().emplace_back(fbi, frame_count);
|
||||||
|
pthread_cond_signal(&m_cond_request);
|
||||||
|
m_fbi_queue.unlock();
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
int getFrameCount(float dt);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void cleanAllFrameBufferImages()
|
||||||
|
{
|
||||||
|
m_fbi_queue.lock();
|
||||||
|
for (auto& p : m_fbi_queue.getData())
|
||||||
|
delete [] p.first;
|
||||||
|
m_fbi_queue.getData().clear();
|
||||||
|
m_fbi_queue.unlock();
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
bool isIdle() const { return m_idle.getAtomic(); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
AVIWriter();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
~AVIWriter();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
static void* startRoutine(void *obj);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
static void setRecordingTarget(const std::string& name)
|
||||||
|
{
|
||||||
|
m_recording_target.setAtomic(name);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void captureFrameBufferImage(float dt);
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void resetFrameBufferImage();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void resetCaptureFormat();
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
void stopRecording() { addFrameBufferImage(NULL, -1); }
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -134,6 +134,8 @@ enum DebugMenuCommand
|
|||||||
DEBUG_SCRIPT_CONSOLE,
|
DEBUG_SCRIPT_CONSOLE,
|
||||||
DEBUG_RUN_CUTSCENE,
|
DEBUG_RUN_CUTSCENE,
|
||||||
DEBUG_TEXTURE_CONSOLE,
|
DEBUG_TEXTURE_CONSOLE,
|
||||||
|
DEBUG_START_RECORDING,
|
||||||
|
DEBUG_STOP_RECORDING
|
||||||
}; // DebugMenuCommand
|
}; // DebugMenuCommand
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -541,7 +543,6 @@ bool handleContextMenuAction(s32 cmd_id)
|
|||||||
break;
|
break;
|
||||||
case DEBUG_VISUAL_VALUES:
|
case DEBUG_VISUAL_VALUES:
|
||||||
{
|
{
|
||||||
#if !defined(__APPLE__)
|
|
||||||
DebugSliderDialog *dsd = new DebugSliderDialog();
|
DebugSliderDialog *dsd = new DebugSliderDialog();
|
||||||
dsd->setSliderHook("red_slider", 0, 255,
|
dsd->setSliderHook("red_slider", 0, 255,
|
||||||
[](){ return int(irr_driver->getAmbientLight().r * 255.f); },
|
[](){ return int(irr_driver->getAmbientLight().r * 255.f); },
|
||||||
@ -576,12 +577,10 @@ bool handleContextMenuAction(s32 cmd_id)
|
|||||||
[](){ return int(irr_driver->getSSAOSigma() * 10.f); },
|
[](){ return int(irr_driver->getSSAOSigma() * 10.f); },
|
||||||
[](int v){irr_driver->setSSAOSigma(v / 10.f); }
|
[](int v){irr_driver->setSSAOSigma(v / 10.f); }
|
||||||
);
|
);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DEBUG_ADJUST_LIGHTS:
|
case DEBUG_ADJUST_LIGHTS:
|
||||||
{
|
{
|
||||||
#if !defined(__APPLE__)
|
|
||||||
// Some sliders use multipliers because the spinner widget
|
// Some sliders use multipliers because the spinner widget
|
||||||
// only supports integers
|
// only supports integers
|
||||||
DebugSliderDialog *dsd = new DebugSliderDialog();
|
DebugSliderDialog *dsd = new DebugSliderDialog();
|
||||||
@ -635,7 +634,6 @@ bool handleContextMenuAction(s32 cmd_id)
|
|||||||
[](int v){ findNearestLight()->setRadius(float(v)); }
|
[](int v){ findNearestLight()->setRadius(float(v)); }
|
||||||
);
|
);
|
||||||
dsd->changeLabel("SSAO Sigma", "[None]");
|
dsd->changeLabel("SSAO Sigma", "[None]");
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DEBUG_SCRIPT_CONSOLE:
|
case DEBUG_SCRIPT_CONSOLE:
|
||||||
@ -711,6 +709,12 @@ bool handleContextMenuAction(s32 cmd_id)
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case DEBUG_START_RECORDING:
|
||||||
|
irr_driver->setRecording(true);
|
||||||
|
break;
|
||||||
|
case DEBUG_STOP_RECORDING:
|
||||||
|
irr_driver->setRecording(false);
|
||||||
|
break;
|
||||||
} // switch
|
} // switch
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -793,8 +797,13 @@ bool onEvent(const SEvent &event)
|
|||||||
sub->addItem(L"Toggle smooth camera", DEBUG_GUI_CAM_SMOOTH);
|
sub->addItem(L"Toggle smooth camera", DEBUG_GUI_CAM_SMOOTH);
|
||||||
sub->addItem(L"Attach fps camera to kart", DEBUG_GUI_CAM_ATTACH);
|
sub->addItem(L"Attach fps camera to kart", DEBUG_GUI_CAM_ATTACH);
|
||||||
|
|
||||||
mnu->addItem(L"Change camera target >",-1,true, true);
|
mnu->addItem(L"Recording >",-1,true, true);
|
||||||
sub = mnu->getSubMenu(4);
|
sub = mnu->getSubMenu(4);
|
||||||
|
sub->addItem(L"Start recording", DEBUG_START_RECORDING);
|
||||||
|
sub->addItem(L"Stop recording", DEBUG_STOP_RECORDING);
|
||||||
|
|
||||||
|
mnu->addItem(L"Change camera target >",-1,true, true);
|
||||||
|
sub = mnu->getSubMenu(5);
|
||||||
sub->addItem(L"To kart one", DEBUG_VIEW_KART_ONE);
|
sub->addItem(L"To kart one", DEBUG_VIEW_KART_ONE);
|
||||||
sub->addItem(L"To kart two", DEBUG_VIEW_KART_TWO);
|
sub->addItem(L"To kart two", DEBUG_VIEW_KART_TWO);
|
||||||
sub->addItem(L"To kart three", DEBUG_VIEW_KART_THREE);
|
sub->addItem(L"To kart three", DEBUG_VIEW_KART_THREE);
|
||||||
@ -805,7 +814,7 @@ bool onEvent(const SEvent &event)
|
|||||||
sub->addItem(L"To kart eight", DEBUG_VIEW_KART_EIGHT);
|
sub->addItem(L"To kart eight", DEBUG_VIEW_KART_EIGHT);
|
||||||
|
|
||||||
mnu->addItem(L"Font >",-1,true, true);
|
mnu->addItem(L"Font >",-1,true, true);
|
||||||
sub = mnu->getSubMenu(5);
|
sub = mnu->getSubMenu(6);
|
||||||
sub->addItem(L"Dump glyph pages of fonts", DEBUG_FONT_DUMP_GLYPH_PAGE);
|
sub->addItem(L"Dump glyph pages of fonts", DEBUG_FONT_DUMP_GLYPH_PAGE);
|
||||||
sub->addItem(L"Reload all fonts", DEBUG_FONT_RELOAD);
|
sub->addItem(L"Reload all fonts", DEBUG_FONT_RELOAD);
|
||||||
|
|
||||||
|
@ -40,6 +40,10 @@
|
|||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__)
|
||||||
|
# include <pthread.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace VS
|
namespace VS
|
||||||
{
|
{
|
||||||
#if defined(_MSC_VER) && defined(DEBUG)
|
#if defined(_MSC_VER) && defined(DEBUG)
|
||||||
@ -77,6 +81,13 @@ namespace VS
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // setThreadName
|
||||||
|
#elif defined(__linux__) && defined(__GLIBC__) && defined(__GLIBC_MINOR__)
|
||||||
|
static void setThreadName(const char* name)
|
||||||
|
{
|
||||||
|
#if __GLIBC__ > 2 || __GLIBC_MINOR__ > 11
|
||||||
|
pthread_setname_np(pthread_self(), name);
|
||||||
|
#endif
|
||||||
} // setThreadName
|
} // setThreadName
|
||||||
#else
|
#else
|
||||||
static void setThreadName(const char* name)
|
static void setThreadName(const char* name)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user