diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 244044412f..71fa7eb934 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -2662,13 +2662,13 @@ reset() { _supports_shadow_filter = _supports_depth_texture; // check if compressed textures are supported - #define CHECK_FOR_DXTVERSION(num) \ - if (_screen->_supported_tex_formats_mask & DXT##num##_FLAG) {\ + #define CHECK_COMPRESSED_FMT(mode, fmt) \ + if (_screen->_supported_tex_formats_mask & fmt##_FLAG) {\ if (dxgsg9_cat.is_debug()) {\ - dxgsg9_cat.debug() << "Compressed texture format DXT" << #num << " supported\n";\ + dxgsg9_cat.debug() << "Compressed texture format " << #fmt << " supported\n";\ }\ _supports_compressed_texture = true;\ - _compressed_texture_formats.set_bit(Texture::CM_dxt##num);\ + _compressed_texture_formats.set_bit(Texture::mode);\ } if (_screen->_intel_compressed_texture_bug) { @@ -2679,14 +2679,16 @@ reset() { } else { // Check for available compressed formats normally. - CHECK_FOR_DXTVERSION(1); - CHECK_FOR_DXTVERSION(2); - CHECK_FOR_DXTVERSION(3); - CHECK_FOR_DXTVERSION(4); - CHECK_FOR_DXTVERSION(5); + CHECK_COMPRESSED_FMT(CM_dxt1, DXT1); + CHECK_COMPRESSED_FMT(CM_dxt2, DXT2); + CHECK_COMPRESSED_FMT(CM_dxt3, DXT3); + CHECK_COMPRESSED_FMT(CM_dxt4, DXT4); + CHECK_COMPRESSED_FMT(CM_dxt5, DXT5); + CHECK_COMPRESSED_FMT(CM_rgtc, ATI1); + CHECK_COMPRESSED_FMT(CM_rgtc, ATI2); } - #undef CHECK_FOR_DXTVERSION + #undef CHECK_COMPRESSED_FMT _screen->_supports_rgba16f_texture_format = false; hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format, 0x0, D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F); @@ -4137,6 +4139,10 @@ void DXGraphicsStateGuardian9:: close_gsg() { GraphicsStateGuardian::close_gsg(); + if (_prepared_objects.is_null()) { + return; + } + if (dxgsg9_cat.is_debug()) { dxgsg9_cat.debug() << "Closing GSG, prepared_objects count = " diff --git a/panda/src/dxgsg9/dxTextureContext9.cxx b/panda/src/dxgsg9/dxTextureContext9.cxx index 9c7421c0ff..d2175abeca 100644 --- a/panda/src/dxgsg9/dxTextureContext9.cxx +++ b/panda/src/dxgsg9/dxTextureContext9.cxx @@ -244,10 +244,11 @@ create_texture(DXScreenData &scrn) { case 1: if (num_alpha_bits > 0) { _d3d_format = D3DFMT_A8; + } else { + _d3d_format = D3DFMT_L8; } break; case 2: - nassertr(false && (num_alpha_bits > 0), false); _d3d_format = D3DFMT_A8L8; break; case 3: @@ -417,12 +418,24 @@ create_texture(DXScreenData &scrn) { case Texture::CM_dxt5: CHECK_FOR_FMT(DXT5); break; + case Texture::CM_rgtc: + if (num_color_channels == 1) { + CHECK_FOR_FMT(ATI1); + } else { + CHECK_FOR_FMT(ATI2); + } + break; } // We don't support the compressed format. Fall through. } if (compress_texture) { + if (num_color_channels == 1) { + CHECK_FOR_FMT(ATI1); + } else if (num_alpha_bits == 0 && num_color_channels == 2) { + CHECK_FOR_FMT(ATI2); + } if (num_alpha_bits <= 1) { CHECK_FOR_FMT(DXT1); } else if (num_alpha_bits <= 4) { @@ -542,7 +555,7 @@ create_texture(DXScreenData &scrn) { CHECK_FOR_FMT(D16); } else { if (scrn._supported_tex_formats_mask & INTZ_FLAG) { - target_pixel_format = (D3DFORMAT)MAKEFOURCC('I', 'N', 'T', 'Z'); + target_pixel_format = D3DFMT_INTZ; goto found_matching_format; } @@ -587,7 +600,7 @@ create_texture(DXScreenData &scrn) { CHECK_FOR_FMT(D32); } else { if (scrn._supported_tex_formats_mask & INTZ_FLAG) { - target_pixel_format = (D3DFORMAT)MAKEFOURCC('I', 'N', 'T', 'Z'); + target_pixel_format = D3DFMT_INTZ; goto found_matching_format; } @@ -716,7 +729,7 @@ create_texture(DXScreenData &scrn) { << "GetDesc failed in create_texture: " << D3DERRORSTRING(hr); } else { if (target_pixel_format != surface_desc.Format && - target_pixel_format != MAKEFOURCC('I', 'N', 'T', 'Z')) { + target_pixel_format != D3DFMT_INTZ) { if (dxgsg9_cat.is_debug()) { dxgsg9_cat.debug() << "Chose format " << D3DFormatStr(surface_desc.Format) @@ -861,6 +874,25 @@ create_texture(DXScreenData &scrn) { << endl; } } + + // DirectX will corrupt memory if we try to load mipmaps smaller than + // 4x4 for ATI1 or ATI2 textures. + if (target_pixel_format == D3DFMT_ATI1 || + target_pixel_format == D3DFMT_ATI2) { + + UINT dimension = min(target_height, target_width); + mip_level_count = 0; + while (dimension >= 4) { + ++mip_level_count; + dimension >>= 1; + } + + if ((UINT)tex->get_num_ram_mipmap_images() < mip_level_count) { + // We also have to generate mipmaps on the CPU for these. + tex->generate_ram_mipmap_images(); + mip_level_count = min(mip_level_count, (UINT)tex->get_num_ram_mipmap_images()); + } + } } else { mip_level_count = 1; } @@ -1254,6 +1286,11 @@ extract_texture_data(DXScreenData &screen) { compression = Texture::CM_dxt5; div = 4; break; + case D3DFMT_ATI1: + case D3DFMT_ATI2: + compression = Texture::CM_rgtc; + div = 4; + break; default: dxgsg9_cat.error() @@ -1682,6 +1719,7 @@ static UINT calculate_row_byte_length (int width, int num_color_channels, D3DFOR // check for compressed textures and adjust source_row_byte_length and source_format accordingly switch (tex_format) { case D3DFMT_DXT1: + case D3DFMT_ATI1: // for dxt1 compressed textures, the row_byte_lenght is "the width of one row of cells, in bytes" // cells are 4 pixels wide, take up 8 bytes, and at least 1 cell has to be there. source_row_byte_length = max(1,width / 4)*8; @@ -1690,6 +1728,7 @@ static UINT calculate_row_byte_length (int width, int num_color_channels, D3DFOR case D3DFMT_DXT3: case D3DFMT_DXT4: case D3DFMT_DXT5: + case D3DFMT_ATI2: // analogue as above, but cells take up 16 bytes source_row_byte_length = max(1,width / 4)*16; break; @@ -1813,15 +1852,27 @@ HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int dep #ifdef DO_PSTATS GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_row_byte_length * height); #endif - hr = D3DXLoadSurfaceFromMemory - (mip_surface, (PALETTEENTRY*)NULL, (RECT*)NULL, (LPCVOID)pixels, - source_format, source_row_byte_length, (PALETTEENTRY*)NULL, - &source_size, mip_filter, (D3DCOLOR)0x0); - if (FAILED(hr)) { - dxgsg9_cat.error() - << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name() - << ", mip_level " << mip_level - << ", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr); + if (source_format == D3DFMT_ATI1 || source_format == D3DFMT_ATI2) { + // These formats are not supported by D3DXLoadSurfaceFromMemory. + D3DLOCKED_RECT rect; + _d3d_2d_texture->LockRect(mip_level, &rect, 0, D3DLOCK_DISCARD); + + unsigned char *dest = (unsigned char *)rect.pBits; + memcpy(dest, pixels, view_size); + + _d3d_2d_texture->UnlockRect(mip_level); + + } else { + hr = D3DXLoadSurfaceFromMemory + (mip_surface, (PALETTEENTRY*)NULL, (RECT*)NULL, (LPCVOID)pixels, + source_format, source_row_byte_length, (PALETTEENTRY*)NULL, + &source_size, mip_filter, (D3DCOLOR)0x0); + if (FAILED(hr)) { + dxgsg9_cat.error() + << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name() + << ", mip_level " << mip_level + << ", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr); + } } exit_FillMipmapSurf: @@ -1950,6 +2001,13 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) { case Texture::CM_dxt5: source_format = D3DFMT_DXT5; break; + case Texture::CM_rgtc: + if (tex->get_num_components() == 1) { + source_format = D3DFMT_ATI1; + } else { + source_format = D3DFMT_ATI2; + } + break; default: // no known compression format.. no adjustment break; @@ -2369,7 +2427,7 @@ d3d_format_to_bytes_per_pixel (D3DFORMAT format) case D3DFMT_D24S8: case D3DFMT_D32: case (D3DFORMAT)MAKEFOURCC('D', 'F', '2', '4'): - case (D3DFORMAT)MAKEFOURCC('I', 'N', 'T', 'Z'): + case D3DFMT_INTZ: bytes_per_pixel = 4.0f; break; @@ -2385,12 +2443,14 @@ d3d_format_to_bytes_per_pixel (D3DFORMAT format) break; case D3DFMT_DXT1: + case D3DFMT_ATI1: bytes_per_pixel = 0.5f; break; case D3DFMT_DXT2: case D3DFMT_DXT3: case D3DFMT_DXT4: case D3DFMT_DXT5: + case D3DFMT_ATI2: bytes_per_pixel = 1.0f; break; } diff --git a/panda/src/dxgsg9/dxgsg9base.h b/panda/src/dxgsg9/dxgsg9base.h index e781fe1f16..56862920b0 100644 --- a/panda/src/dxgsg9/dxgsg9base.h +++ b/panda/src/dxgsg9/dxgsg9base.h @@ -172,8 +172,8 @@ typedef enum { INTZ_FLAG = FLG(22), W11V11U10_FLAG = FLG(23), A2W10V10U10_FLAG = FLG(24), - UYVY_FLAG = FLG(25), - YUY2_FLAG = FLG(26), + ATI1_FLAG = FLG(25), + ATI2_FLAG = FLG(26), DXT1_FLAG = FLG(27), DXT2_FLAG = FLG(28), DXT3_FLAG = FLG(29), @@ -181,6 +181,10 @@ typedef enum { DXT5_FLAG = FLG(31) } D3DFORMAT_FLAG; +#define D3DFMT_INTZ ((D3DFORMAT)MAKEFOURCC('I', 'N', 'T', 'Z')) +#define D3DFMT_ATI1 ((D3DFORMAT)MAKEFOURCC('A', 'T', 'I', '1')) +#define D3DFMT_ATI2 ((D3DFORMAT)MAKEFOURCC('A', 'T', 'I', '2')) + // this is only used in conjunction w/rendertgt fmts, so just make it something that can never be a rtgt #define DISPLAY_32BPP_REQUIRES_16BPP_ZBUFFER_FLAG DXT1_FLAG #define DISPLAY_16BPP_REQUIRES_16BPP_ZBUFFER_FLAG DXT2_FLAG diff --git a/panda/src/dxgsg9/wdxGraphicsPipe9.cxx b/panda/src/dxgsg9/wdxGraphicsPipe9.cxx index 4fe81dad5c..0ea30da942 100644 --- a/panda/src/dxgsg9/wdxGraphicsPipe9.cxx +++ b/panda/src/dxgsg9/wdxGraphicsPipe9.cxx @@ -894,12 +894,12 @@ void Init_D3DFORMAT_map() { INSERT_ELEM(D24X8); INSERT_ELEM(D24S8); INSERT_ELEM(D32); - g_D3DFORMATmap[INTZ_FLAG] = (D3DFORMAT)MAKEFOURCC('I', 'N', 'T', 'Z'); + INSERT_ELEM(INTZ); // NOT IN DX9 // INSERT_ELEM(W11V11U10); INSERT_ELEM(A2W10V10U10); - INSERT_ELEM(UYVY); - INSERT_ELEM(YUY2); + INSERT_ELEM(ATI1); + INSERT_ELEM(ATI2); INSERT_ELEM(DXT1); INSERT_ELEM(DXT2); INSERT_ELEM(DXT3); @@ -940,8 +940,8 @@ const char *D3DFormatStr(D3DFORMAT fmt) { // NOT IN DX9 // CASESTR(D3DFMT_W11V11U10); CASESTR(D3DFMT_A2W10V10U10); - CASESTR(D3DFMT_UYVY); - CASESTR(D3DFMT_YUY2); + CASESTR(D3DFMT_ATI1); + CASESTR(D3DFMT_ATI2); CASESTR(D3DFMT_DXT1); CASESTR(D3DFMT_DXT2); CASESTR(D3DFMT_DXT3); @@ -954,6 +954,7 @@ const char *D3DFormatStr(D3DFORMAT fmt) { CASESTR(D3DFMT_D16); CASESTR(D3DFMT_D24X8); CASESTR(D3DFMT_D24X4S4); + CASESTR(D3DFMT_INTZ); CASESTR(D3DFMT_VERTEXDATA); CASESTR(D3DFMT_INDEX16); CASESTR(D3DFMT_INDEX32); diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 8b19191028..ffb8b933c7 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1035,6 +1035,16 @@ reset() { break; } } + +#ifndef OPENGLES + // The OpenGL spec states that these are not reported by the above + // mechanism, so we have to check for the extension ourselves. + if (is_at_least_gl_version(3, 0) || + has_extension("GL_ARB_texture_compression_rgtc") || + has_extension("GL_EXT_texture_compression_rgtc")) { + _compressed_texture_formats.set_bit(Texture::CM_rgtc); + } +#endif } #ifdef OPENGLES_2 @@ -8115,6 +8125,8 @@ get_component_type(Texture::ComponentType component_type) { return GL_BYTE; case Texture::T_short: return GL_SHORT; + case Texture::T_half_float: + return GL_HALF_FLOAT; default: GLCAT.error() << "Invalid Texture::Type value!\n"; return GL_UNSIGNED_BYTE; @@ -8144,6 +8156,7 @@ get_external_image_format(Texture *tex) const { case Texture::F_depth_component32: case Texture::F_depth_stencil: case Texture::F_r11_g11_b10: + case Texture::F_rgb9_e5: // This shouldn't be possible. nassertr(false, GL_RGB); break; @@ -8156,6 +8169,7 @@ get_external_image_format(Texture *tex) const { case Texture::F_rgba16: case Texture::F_rgba32: case Texture::F_rgba8i: + case Texture::F_rgb10_a2: return GL_COMPRESSED_RGBA; case Texture::F_rgb: @@ -8181,6 +8195,7 @@ get_external_image_format(Texture *tex) const { case Texture::F_r32i: return GL_COMPRESSED_RED; + case Texture::F_rg: case Texture::F_rg8i: case Texture::F_rg16: case Texture::F_rg32: @@ -8274,6 +8289,20 @@ get_external_image_format(Texture *tex) const { break; #endif // OPENGLES + case Texture::CM_rgtc: +#ifndef OPENGLES + if (tex->get_format() == Texture::F_luminance) { + return GL_COMPRESSED_LUMINANCE_LATC1_EXT; + } else if (tex->get_format() == Texture::F_luminance_alpha) { + return GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; + } else if (tex->get_num_components() == 1) { + return GL_COMPRESSED_RED_RGTC1; + } else { + return GL_COMPRESSED_RG_RGTC2; + } +#endif + break; + case Texture::CM_default: case Texture::CM_off: case Texture::CM_dxt2: @@ -8315,6 +8344,7 @@ get_external_image_format(Texture *tex) const { #endif #ifndef OPENGLES_1 + case Texture::F_rg: case Texture::F_rg16: case Texture::F_rg32: return GL_RG; @@ -8328,6 +8358,7 @@ get_external_image_format(Texture *tex) const { case Texture::F_rgb32: case Texture::F_srgb: case Texture::F_r11_g11_b10: + case Texture::F_rgb9_e5: #ifdef OPENGLES return GL_RGB; #else @@ -8342,6 +8373,7 @@ get_external_image_format(Texture *tex) const { case Texture::F_rgba16: case Texture::F_rgba32: case Texture::F_srgb_alpha: + case Texture::F_rgb10_a2: #ifdef OPENGLES_2 return GL_RGBA; #else @@ -8427,10 +8459,12 @@ get_internal_image_format(Texture *tex, bool force_sized) const { case Texture::F_rgba8i: case Texture::F_r32i: case Texture::F_r11_g11_b10: + case Texture::F_rgb9_e5: // Unsupported; fall through to below. break; case Texture::F_rgbm: + case Texture::F_rgb10_a2: if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) { return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; } @@ -8512,7 +8546,9 @@ get_internal_image_format(Texture *tex, bool force_sized) const { case Texture::F_blue: case Texture::F_r16: case Texture::F_r32: - if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) { + if (get_supports_compressed_texture_format(Texture::CM_rgtc) && !is_3d) { + return GL_COMPRESSED_RED_RGTC1; + } else if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) { return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; } #ifndef OPENGLES @@ -8523,9 +8559,12 @@ get_internal_image_format(Texture *tex, bool force_sized) const { #endif break; + case Texture::F_rg: case Texture::F_rg16: case Texture::F_rg32: - if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) { + if (get_supports_compressed_texture_format(Texture::CM_rgtc) && !is_3d) { + return GL_COMPRESSED_RG_RGTC2; + } else if (get_supports_compressed_texture_format(Texture::CM_dxt1) && !is_3d) { return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; } #ifndef OPENGLES @@ -8657,6 +8696,20 @@ get_internal_image_format(Texture *tex, bool force_sized) const { break; #endif + case Texture::CM_rgtc: +#ifndef OPENGLES + if (tex->get_format() == Texture::F_luminance) { + return GL_COMPRESSED_LUMINANCE_LATC1_EXT; + } else if (tex->get_format() == Texture::F_luminance_alpha) { + return GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; + } else if (tex->get_num_components() == 1) { + return GL_COMPRESSED_RED_RGTC1; + } else if (tex->get_num_components() == 2) { + return GL_COMPRESSED_RG_RGTC2; + } +#endif + break; + case Texture::CM_default: case Texture::CM_off: case Texture::CM_dxt2: @@ -8942,6 +8995,9 @@ get_internal_image_format(Texture *tex, bool force_sized) const { return force_sized ? GL_RG8 : GL_RG; #endif + case Texture::F_rg: + return force_sized ? GL_RG8 : GL_RG; + #ifndef OPENGLES_1 case Texture::F_srgb: #ifndef OPENGLES @@ -8965,6 +9021,12 @@ get_internal_image_format(Texture *tex, bool force_sized) const { #ifndef OPENGLES case Texture::F_r11_g11_b10: return GL_R11F_G11F_B10F; + + case Texture::F_rgb9_e5: + return GL_RGB9_E5; + + case Texture::F_rgb10_a2: + return GL_RGB10_A2; #endif default: @@ -9018,8 +9080,19 @@ is_compressed_format(GLenum format) { case GL_COMPRESSED_RGB_FXT1_3DFX: case GL_COMPRESSED_RGBA_FXT1_3DFX: + case GL_COMPRESSED_RED_RGTC1: + case GL_COMPRESSED_SIGNED_RED_RGTC1: + case GL_COMPRESSED_RG_RGTC2: + case GL_COMPRESSED_SIGNED_RG_RGTC2: + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: + case GL_COMPRESSED_RGB: + case GL_COMPRESSED_SRGB_EXT: case GL_COMPRESSED_RGBA: + case GL_COMPRESSED_SRGB_ALPHA_EXT: case GL_COMPRESSED_ALPHA: case GL_COMPRESSED_LUMINANCE: case GL_COMPRESSED_LUMINANCE_ALPHA: @@ -11170,6 +11243,10 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { if (!get_supports_compressed_texture_format(image_compression)) { image = tex->get_uncompressed_ram_image(); image_compression = Texture::CM_off; + + // If this triggers, Panda cannot decompress the texture. Compile + // with libsquish support or precompress the texture. + nassertr(!image.is_null(), false); } int mipmap_bias = 0; @@ -12620,6 +12697,16 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { type = Texture::T_float; format = Texture::F_r11_g11_b10; break; + + case GL_RGB9_E5: + type = Texture::T_float; + format = Texture::F_rgb9_e5; + break; + + case GL_RGB10_A2: + type = Texture::T_unsigned_short; + format = Texture::F_rgb10_a2; + break; #endif #ifdef OPENGLES_2 @@ -12790,6 +12877,32 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { format = Texture::F_rgba; compression = Texture::CM_fxt1; break; + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + format = Texture::F_luminance; + compression = Texture::CM_rgtc; + break; + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + format = Texture::F_luminance_alpha; + compression = Texture::CM_rgtc; + break; + case GL_COMPRESSED_RED_RGTC1: + format = Texture::F_red; + compression = Texture::CM_rgtc; + break; + case GL_COMPRESSED_SIGNED_RED_RGTC1: + type = Texture::T_byte; + format = Texture::F_red; + compression = Texture::CM_rgtc; + break; + case GL_COMPRESSED_RG_RGTC2: + format = Texture::F_rg; + compression = Texture::CM_rgtc; + break; + case GL_COMPRESSED_SIGNED_RG_RGTC2: + type = Texture::T_byte; + format = Texture::F_rg; + compression = Texture::CM_rgtc; + break; #endif default: GLCAT.warning() diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index c0ae51c523..46cfdee17d 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -513,6 +513,7 @@ estimate_texture_memory() const { case Texture::F_rgba4: case Texture::F_rgb5: case Texture::F_rgba5: + case Texture::F_rg: bpp = 2; break; @@ -585,6 +586,8 @@ estimate_texture_memory() const { break; case Texture::F_r11_g11_b10: + case Texture::F_rgb9_e5: + case Texture::F_rgb10_a2: bpp = 4; break; @@ -1570,6 +1573,8 @@ write(ostream &out, int indent_level) const { out << " shorts"; break; + case T_half_float: + out << " half"; case T_float: out << " floats"; break; @@ -1719,6 +1724,16 @@ write(ostream &out, int indent_level) const { case F_r11_g11_b10: out << "r11_g11_b10"; break; + case F_rgb9_e5: + out << "rgb9_e5"; + break; + case F_rgb10_a2: + out << "rgb10_a2"; + break; + + case F_rg: + out << "rg"; + break; } if (cdata->_compression != CM_default) { @@ -2016,6 +2031,8 @@ format_component_type(ComponentType ct) { return "unsigned_byte"; case T_short: return "short"; + case T_half_float: + return "half_float"; } return "**invalid**"; @@ -2043,6 +2060,8 @@ string_component_type(const string &str) { return T_byte; } else if (cmp_nocase(str, "short") == 0) { return T_short; + } else if (cmp_nocase(str, "half_float") == 0) { + return T_half_float; } gobj_cat->error() @@ -2143,6 +2162,12 @@ format_format(Format format) { return "rgba8i"; case F_r11_g11_b10: return "r11g11b10"; + case F_rgb9_e5: + return "rgb9_e5"; + case F_rgb10_a2: + return "rgb10_a2"; + case F_rg: + return "rg"; } return "**invalid**"; } @@ -2231,6 +2256,12 @@ string_format(const string &str) { return F_rgb32; } else if (cmp_nocase(str, "r11g11b10") == 0) { return F_r11_g11_b10; + } else if (cmp_nocase(str, "rgb9_e5") == 0) { + return F_rgb9_e5; + } else if (cmp_nocase_uh(str, "rgb10_a2") == 0 || cmp_nocase(str, "r10g10b10a2") == 0) { + return F_rgb10_a2; + } else if (cmp_nocase_uh(str, "rg") == 0) { + return F_rg; } gobj_cat->error() @@ -2269,6 +2300,8 @@ format_compression_mode(CompressionMode cm) { return "pvr1_2bpp"; case CM_pvr1_4bpp: return "pvr1_4bpp"; + case CM_rgtc: + return "rgtc"; } return "**invalid**"; @@ -2304,6 +2337,8 @@ string_compression_mode(const string &str) { return CM_pvr1_2bpp; } else if (cmp_nocase_uh(str, "pvr1_4bpp") == 0) { return CM_pvr1_4bpp; + } else if (cmp_nocase_uh(str, "rgtc") == 0) { + return CM_rgtc; } gobj_cat->error() @@ -2484,6 +2519,8 @@ has_alpha(Format format) { case F_luminance_alphamask: case F_srgb_alpha: case F_sluminance_alpha: + case F_rgba8i: + case F_rgb10_a2: return true; default: @@ -3574,11 +3611,196 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) ReadDDSLevelFunc func = NULL; Format format = F_rgb; + ComponentType component_type = T_unsigned_byte; do_clear_ram_image(cdata); CompressionMode compression = CM_off; - if (header.pf.pf_flags & DDPF_FOURCC) { + if ((header.pf.pf_flags & DDPF_FOURCC) != 0 && + header.pf.four_cc == 0x30315844) { // 'DX10' + // A DirectX 10 style texture, which has an additional header. + func = read_dds_level_generic_uncompressed; + unsigned int format = dds.get_uint32(); + unsigned int dimension = dds.get_uint32(); + unsigned int misc_flag = dds.get_uint32(); + unsigned int array_size = dds.get_uint32(); + unsigned int alpha_mode = dds.get_uint32(); + + switch (format) { + case 2: // DXGI_FORMAT_R32G32B32A32_FLOAT + format = F_rgba32; + component_type = T_float; + func = read_dds_level_abgr32; + break; + case 10: // DXGI_FORMAT_R16G16B16A16_FLOAT + format = F_rgba16; + component_type = T_half_float; + func = read_dds_level_abgr16; + break; + case 11: // DXGI_FORMAT_R16G16B16A16_UNORM + format = F_rgba16; + component_type = T_unsigned_short; + func = read_dds_level_abgr16; + break; + case 27: // DXGI_FORMAT_R8G8B8A8_TYPELESS + case 28: // DXGI_FORMAT_R8G8B8A8_UNORM + format = F_rgba8; + func = read_dds_level_abgr8; + break; + case 29: // DXGI_FORMAT_R8G8B8A8_UNORM_SRGB + format = F_srgb_alpha; + func = read_dds_level_abgr8; + break; + case 30: // DXGI_FORMAT_R8G8B8A8_UINT + format = F_rgba8i; + func = read_dds_level_abgr8; + break; + case 31: // DXGI_FORMAT_R8G8B8A8_SNORM + format = F_rgba8; + component_type = T_byte; + func = read_dds_level_abgr8; + break; + case 32: // DXGI_FORMAT_R8G8B8A8_SINT + format = F_rgba8i; + component_type = T_byte; + func = read_dds_level_abgr8; + break; + case 48: // DXGI_FORMAT_R8G8_TYPELESS + case 49: // DXGI_FORMAT_R8G8_UNORM + format = F_rg; + break; + case 50: // DXGI_FORMAT_R8G8_UINT + format = F_rg8i; + break; + case 51: // DXGI_FORMAT_R8G8_SNORM + format = F_rg; + component_type = T_byte; + break; + case 52: // DXGI_FORMAT_R8G8_SINT + format = F_rg8i; + component_type = T_byte; + break; + case 60: // DXGI_FORMAT_R8_TYPELESS + case 61: // DXGI_FORMAT_R8_UNORM + format = F_red; + break; + case 62: // DXGI_FORMAT_R8_UINT + format = F_r8i; + break; + case 63: // DXGI_FORMAT_R8_SNORM + format = F_red; + component_type = T_byte; + break; + case 64: // DXGI_FORMAT_R8_SINT + format = F_r8i; + component_type = T_byte; + break; + case 65: // DXGI_FORMAT_A8_UNORM + format = F_alpha; + break; + case 70: // DXGI_FORMAT_BC1_TYPELESS + case 71: // DXGI_FORMAT_BC1_UNORM + format = F_rgb; + compression = CM_dxt1; + func = read_dds_level_bc1; + break; + case 72: // DXGI_FORMAT_BC1_UNORM_SRGB + format = F_srgb; + compression = CM_dxt1; + func = read_dds_level_bc1; + break; + case 73: // DXGI_FORMAT_BC2_TYPELESS + case 74: // DXGI_FORMAT_BC2_UNORM + format = F_rgba; + compression = CM_dxt3; + func = read_dds_level_bc2; + break; + case 75: // DXGI_FORMAT_BC2_UNORM_SRGB + format = F_srgb_alpha; + compression = CM_dxt3; + func = read_dds_level_bc2; + break; + case 76: // DXGI_FORMAT_BC3_TYPELESS + case 77: // DXGI_FORMAT_BC3_UNORM + format = F_rgba; + compression = CM_dxt5; + func = read_dds_level_bc3; + break; + case 78: // DXGI_FORMAT_BC3_UNORM_SRGB + format = F_srgb_alpha; + compression = CM_dxt5; + func = read_dds_level_bc3; + break; + case 79: // DXGI_FORMAT_BC4_TYPELESS + case 80: // DXGI_FORMAT_BC4_UNORM + format = F_red; + compression = CM_rgtc; + func = read_dds_level_bc4; + break; + break; + case 82: // DXGI_FORMAT_BC5_TYPELESS + case 83: // DXGI_FORMAT_BC5_UNORM + format = F_rg; + compression = CM_rgtc; + func = read_dds_level_bc5; + break; + case 87: // DXGI_FORMAT_B8G8R8A8_UNORM + case 90: // DXGI_FORMAT_B8G8R8A8_TYPELESS + format = F_rgba8; + break; + case 88: // DXGI_FORMAT_B8G8R8X8_UNORM + case 92: // DXGI_FORMAT_B8G8R8X8_TYPELESS + format = F_rgb8; + break; + case 91: // DXGI_FORMAT_B8G8R8A8_UNORM_SRGB + format = F_srgb_alpha; + break; + case 93: // DXGI_FORMAT_B8G8R8X8_UNORM_SRGB + format = F_srgb; + break; + case 115: // DXGI_FORMAT_B4G4R4A4_UNORM + format = F_rgba4; + break; + default: + gobj_cat.error() + << filename << ": unsupported DXGI format " << format << ".\n"; + return false; + } + + switch (dimension) { + case 2: // DDS_DIMENSION_TEXTURE1D + texture_type = TT_1d_texture; + header.depth = 1; + break; + case 3: // DDS_DIMENSION_TEXTURE2D + if (misc_flag & 0x4) { // DDS_RESOURCE_MISC_TEXTURECUBE + if (array_size > 1) { + texture_type = TT_cube_map_array; + header.depth = array_size * 6; + } else { + texture_type = TT_cube_map; + header.depth = 6; + } + } else { + if (array_size > 1) { + texture_type = TT_2d_texture_array; + header.depth = array_size; + } else { + texture_type = TT_2d_texture; + header.depth = 1; + } + } + break; + case 4: // DDS_DIMENSION_TEXTURE3D + texture_type = TT_3d_texture; + break; + default: + gobj_cat.error() + << filename << ": unsupported dimension.\n"; + return false; + } + + } else if (header.pf.pf_flags & DDPF_FOURCC) { // Some compressed texture format. if (texture_type == TT_3d_texture) { gobj_cat.error() @@ -3586,31 +3808,69 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) return false; } - if (header.pf.four_cc == 0x31545844) { // 'DXT1', little-endian. + // Most of the compressed formats support alpha. + format = F_rgba; + switch (header.pf.four_cc) { + case 0x31545844: // 'DXT1', little-endian. compression = CM_dxt1; - func = read_dds_level_dxt1; - } else if (header.pf.four_cc == 0x32545844) { // 'DXT2' + func = read_dds_level_bc1; + format = F_rgbm; + break; + case 0x32545844: // 'DXT2' compression = CM_dxt2; - func = read_dds_level_dxt23; - } else if (header.pf.four_cc == 0x33545844) { // 'DXT3' + func = read_dds_level_bc2; + break; + case 0x33545844: // 'DXT3' compression = CM_dxt3; - func = read_dds_level_dxt23; - } else if (header.pf.four_cc == 0x34545844) { // 'DXT4' + func = read_dds_level_bc2; + break; + case 0x34545844: // 'DXT4' compression = CM_dxt4; - func = read_dds_level_dxt45; - } else if (header.pf.four_cc == 0x35545844) { // 'DXT5' + func = read_dds_level_bc3; + break; + case 0x35545844: // 'DXT5' compression = CM_dxt5; - func = read_dds_level_dxt45; - } else { + func = read_dds_level_bc3; + break; + case 0x31495441: // 'ATI1' + case 0x55344342: // 'BC4U' + compression = CM_rgtc; + func = read_dds_level_bc4; + format = F_red; + break; + case 0x32495441: // 'ATI2' + case 0x55354342: // 'BC5U' + compression = CM_rgtc; + func = read_dds_level_bc5; + format = F_rg; + break; + case 36: // D3DFMT_A16B16G16R16 + func = read_dds_level_abgr16; + format = F_rgba16; + component_type = T_unsigned_short; + break; + case 110: // D3DFMT_Q16W16V16U16 + func = read_dds_level_abgr16; + format = F_rgba16; + component_type = T_short; + break; + case 113: // D3DFMT_A16B16G16R16F + func = read_dds_level_abgr16; + format = F_rgba16; + component_type = T_half_float; + break; + case 116: // D3DFMT_A32B32G32R32F + func = read_dds_level_abgr32; + format = F_rgba32; + component_type = T_float; + break; + default: gobj_cat.error() - << filename << ": unsupported texture compression.\n"; + << filename << ": unsupported texture compression (FourCC: 0x" + << hex << header.pf.four_cc << dec << ").\n"; return false; } - // All of the compressed formats support alpha, even DXT1 (to some - // extent, at least). - format = F_rgba; - } else { // An uncompressed texture format. func = read_dds_level_generic_uncompressed; @@ -3660,7 +3920,7 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) } do_setup_texture(cdata, texture_type, header.width, header.height, header.depth, - T_unsigned_byte, format); + component_type, format); cdata->_orig_file_x_size = cdata->_x_size; cdata->_orig_file_y_size = cdata->_y_size; @@ -3744,6 +4004,44 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) } break; + case TT_2d_texture_array: + case TT_cube_map_array: //TODO: rearrange cube map array faces? + { + // Texture arrays store all the mipmap levels for layer 0, then + // all the mipmap levels for layer 1, and so on. + pvector > pages; + pages.reserve(header.depth); + int z, n; + for (z = 0; z < (int)header.depth; ++z) { + pages.push_back(pvector()); + pvector &levels = pages.back(); + levels.reserve(header.num_levels); + + for (n = 0; n < (int)header.num_levels; ++n) { + PTA_uchar image = func(this, cdata, header, n, in); + if (image.is_null()) { + return false; + } + levels.push_back(image); + } + } + + // Now, for each level, reassemble the pages into one big + // image. + for (n = 0; n < (int)header.num_levels; ++n) { + size_t page_size = pages[0][n].size(); + PTA_uchar image = PTA_uchar::empty_array(page_size * header.depth); + unsigned char *imagep = (unsigned char *)image.p(); + for (z = 0; z < (int)header.depth; ++z) { + nassertr(pages[z][n].size() == page_size, false); + memcpy(imagep + z * page_size, pages[z][n].p(), page_size); + } + + do_set_ram_mipmap_image(cdata, n, image, page_size); + } + } + break; + default: // Normal 2-d textures simply store the mipmap levels. { @@ -4673,6 +4971,10 @@ do_compress_ram_image(CData *cdata, Texture::CompressionMode compression, GraphicsStateGuardianBase *gsg) { nassertr(compression != CM_off, false); + if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) { + return false; + } + if (compression == CM_on) { // Select an appropriate compression mode automatically. switch (cdata->_format) { @@ -4685,6 +4987,7 @@ do_compress_ram_image(CData *cdata, Texture::CompressionMode compression, case Texture::F_rgb332: case Texture::F_rgb16: case Texture::F_rgb32: + case Texture::F_rgb10_a2: if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt1)) { compression = CM_dxt1; } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt3)) { @@ -4712,6 +5015,13 @@ do_compress_ram_image(CData *cdata, Texture::CompressionMode compression, } break; + case Texture::F_red: + case Texture::F_rg: + if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_rgtc)) { + compression = CM_rgtc; + } + break; + default: break; } @@ -4725,6 +5035,77 @@ do_compress_ram_image(CData *cdata, Texture::CompressionMode compression, quality_level = texture_quality_level; } + if (compression == CM_rgtc) { + // We should compress RGTC ourselves, as squish does not support it. + if (cdata->_component_type != T_unsigned_byte) { + return false; + } + + if (!do_has_all_ram_mipmap_images(cdata)) { + // If we're about to compress the RAM image, we should ensure that + // we have all of the mipmap levels first. + do_generate_ram_mipmap_images(cdata); + } + + RamImages compressed_ram_images; + compressed_ram_images.resize(cdata->_ram_images.size()); + + for (size_t n = 0; n < cdata->_ram_images.size(); ++n) { + const RamImage *uncompressed_image = &cdata->_ram_images[n]; + + int x_size = do_get_expected_mipmap_x_size(cdata, n); + int y_size = do_get_expected_mipmap_y_size(cdata, n); + int num_pages = do_get_expected_mipmap_num_pages(cdata, n); + + // It is important that we handle image sizes that aren't a multiple + // of the block size, since this method may be used to compress + // mipmaps, which go all the way to 1x1. Pad the image if necessary. + RamImage temp_image; + if ((x_size | y_size) & 0x3) { + int virtual_x_size = x_size; + int virtual_y_size = y_size; + x_size = (x_size + 3) & ~0x3; + y_size = (y_size + 3) & ~0x3; + + temp_image._page_size = x_size * y_size * cdata->_num_components; + temp_image._image = PTA_uchar::empty_array(temp_image._page_size * num_pages); + + for (int z = 0; z < num_pages; ++z) { + unsigned char *dest = temp_image._image.p() + z * temp_image._page_size; + unsigned const char *src = uncompressed_image->_image.p() + z * uncompressed_image->_page_size; + + for (int y = 0; y < virtual_y_size; ++y) { + memcpy(dest, src, virtual_x_size); + src += virtual_x_size; + dest += x_size; + } + } + + uncompressed_image = &temp_image; + } + + // Create a new image to hold the compressed texture pages. + RamImage &compressed_image = compressed_ram_images[n]; + compressed_image._page_size = (x_size * y_size * cdata->_num_components) >> 1; + compressed_image._image = PTA_uchar::empty_array(compressed_image._page_size * num_pages); + + if (cdata->_num_components == 1) { + do_compress_ram_image_bc4(*uncompressed_image, compressed_image, + x_size, y_size, num_pages); + } else if (cdata->_num_components == 2) { + do_compress_ram_image_bc5(*uncompressed_image, compressed_image, + x_size, y_size, num_pages); + } else { + // Invalid. + return false; + } + } + + cdata->_ram_images.swap(compressed_ram_images); + cdata->_ram_image_compression = CM_rgtc; + return true; + } + #ifdef HAVE_SQUISH if (cdata->_texture_type != TT_3d_texture && cdata->_texture_type != TT_2d_texture_array && @@ -4785,6 +5166,39 @@ do_compress_ram_image(CData *cdata, Texture::CompressionMode compression, //////////////////////////////////////////////////////////////////// bool Texture:: do_uncompress_ram_image(CData *cdata) { + nassertr(!cdata->_ram_images.empty(), false); + + if (cdata->_ram_image_compression == CM_rgtc) { + // We should decompress RGTC ourselves, as squish doesn't support it. + RamImages uncompressed_ram_images; + uncompressed_ram_images.resize(cdata->_ram_images.size()); + + for (size_t n = 0; n < cdata->_ram_images.size(); ++n) { + const RamImage &compressed_image = cdata->_ram_images[n]; + + int x_size = do_get_expected_mipmap_x_size(cdata, n); + int y_size = do_get_expected_mipmap_y_size(cdata, n); + int num_pages = do_get_expected_mipmap_num_pages(cdata, n); + + RamImage &uncompressed_image = uncompressed_ram_images[n]; + uncompressed_image._page_size = do_get_expected_ram_mipmap_page_size(cdata, n); + uncompressed_image._image = PTA_uchar::empty_array(uncompressed_image._page_size * num_pages); + + if (cdata->_num_components == 1) { + do_uncompress_ram_image_bc4(compressed_image, uncompressed_image, + x_size, y_size, num_pages); + } else if (cdata->_num_components == 2) { + do_uncompress_ram_image_bc5(compressed_image, uncompressed_image, + x_size, y_size, num_pages); + } else { + // Invalid. + return false; + } + } + cdata->_ram_images.swap(uncompressed_ram_images); + cdata->_ram_image_compression = CM_off; + return true; + } #ifdef HAVE_SQUISH if (cdata->_texture_type != TT_3d_texture && @@ -4819,6 +5233,441 @@ do_uncompress_ram_image(CData *cdata) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_compress_ram_image_bc4 +// Access: Protected, Static +// Description: Compresses a RAM image using BC4 compression. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_compress_ram_image_bc4(const RamImage &uncompressed_image, + RamImage &compressed_image, + int x_size, int y_size, int num_pages) { + int x_blocks = (x_size >> 2); + int y_blocks = (y_size >> 2); + + //NB. This algorithm isn't fully optimal, since it doesn't try to make + // use of the secondary interpolation mode supported by BC4. This is + // not important for most textures, but it may be added in the future. + + nassertv(x_blocks * y_blocks * 4 * 4 <= uncompressed_image._page_size); + nassertv(x_size * y_size == uncompressed_image._page_size); + + static const int remap[] = {1, 7, 6, 5, 4, 3, 2, 0}; + + for (int z = 0; z < num_pages; ++z) { + unsigned char *dest = compressed_image._image.p() + z * compressed_image._page_size; + unsigned const char *src = uncompressed_image._image.p() + z * uncompressed_image._page_size; + + // Convert one 4 x 4 block at a time. + for (int y = 0; y < y_blocks; ++y) { + for (int x = 0; x < x_blocks; ++x) { + int a, b, c, d; + float fac, add; + unsigned char minv, maxv; + unsigned const char *blk = src; + + // Find the minimum and maximum value in the block. + minv = blk[0]; + maxv = blk[0]; + minv = min(blk[1], minv); maxv = max(blk[1], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[3], minv); maxv = max(blk[3], maxv); + blk += x_size; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[1], minv); maxv = max(blk[1], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[3], minv); maxv = max(blk[3], maxv); + blk += x_size; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[1], minv); maxv = max(blk[1], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[3], minv); maxv = max(blk[3], maxv); + blk += x_size; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[1], minv); maxv = max(blk[1], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[3], minv); maxv = max(blk[3], maxv); + + // Now calculate the index for each pixel. + blk = src; + if (maxv > minv) { + fac = 7.5f / (maxv - minv); + } else { + fac = 0; + } + add = -minv * fac; + a = (remap[(int)(blk[0] * fac + add)]) + | (remap[(int)(blk[1] * fac + add)] << 3) + | (remap[(int)(blk[2] * fac + add)] << 6) + | (remap[(int)(blk[3] * fac + add)] << 9); + blk += x_size; + b = (remap[(int)(blk[0] * fac + add)] << 4) + | (remap[(int)(blk[1] * fac + add)] << 7) + | (remap[(int)(blk[2] * fac + add)] << 10) + | (remap[(int)(blk[3] * fac + add)] << 13); + blk += x_size; + c = (remap[(int)(blk[0] * fac + add)]) + | (remap[(int)(blk[1] * fac + add)] << 3) + | (remap[(int)(blk[2] * fac + add)] << 6) + | (remap[(int)(blk[3] * fac + add)] << 9); + blk += x_size; + d = (remap[(int)(blk[0] * fac + add)] << 4) + | (remap[(int)(blk[1] * fac + add)] << 7) + | (remap[(int)(blk[2] * fac + add)] << 10) + | (remap[(int)(blk[3] * fac + add)] << 13); + + *(dest++) = maxv; + *(dest++) = minv; + *(dest++) = a & 0xff; + *(dest++) = (a >> 8) | (b & 0xf0); + *(dest++) = b >> 8; + *(dest++) = c & 0xff; + *(dest++) = (c >> 8) | (d & 0xf0); + *(dest++) = d >> 8; + + // Advance to the beginning of the next 4x4 block. + src += 4; + } + src += x_size * 3; + } + Thread::consider_yield(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_compress_ram_image_bc5 +// Access: Protected, Static +// Description: Compresses a RAM image using BC5 compression. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_compress_ram_image_bc5(const RamImage &uncompressed_image, + RamImage &compressed_image, + int x_size, int y_size, int num_pages) { + int x_blocks = (x_size >> 2); + int y_blocks = (y_size >> 2); + int stride = x_size * 2; + + // BC5 uses the same compression algorithm as BC4, except repeated + // for two channels. + + nassertv(x_blocks * y_blocks * 4 * 4 * 2 <= uncompressed_image._page_size); + nassertv(stride * y_size == uncompressed_image._page_size); + + static const int remap[] = {1, 7, 6, 5, 4, 3, 2, 0}; + + for (int z = 0; z < num_pages; ++z) { + unsigned char *dest = compressed_image._image.p() + z * compressed_image._page_size; + unsigned const char *src = uncompressed_image._image.p() + z * uncompressed_image._page_size; + + // Convert one 4 x 4 block at a time. + for (int y = 0; y < y_blocks; ++y) { + for (int x = 0; x < x_blocks; ++x) { + int a, b, c, d; + float fac, add; + unsigned char minv, maxv; + unsigned const char *blk = src; + + // Find the minimum and maximum red value in the block. + minv = blk[0]; + maxv = blk[0]; + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + blk += stride; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + blk += stride; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + blk += stride; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + + // Now calculate the index for each pixel. + if (maxv > minv) { + fac = 7.5f / (maxv - minv); + } else { + fac = 0; + } + add = -minv * fac; + blk = src; + a = (remap[(int)(blk[0] * fac + add)]) + | (remap[(int)(blk[2] * fac + add)] << 3) + | (remap[(int)(blk[4] * fac + add)] << 6) + | (remap[(int)(blk[6] * fac + add)] << 9); + blk += stride; + b = (remap[(int)(blk[0] * fac + add)] << 4) + | (remap[(int)(blk[2] * fac + add)] << 7) + | (remap[(int)(blk[4] * fac + add)] << 10) + | (remap[(int)(blk[6] * fac + add)] << 13); + blk += stride; + c = (remap[(int)(blk[0] * fac + add)]) + | (remap[(int)(blk[2] * fac + add)] << 3) + | (remap[(int)(blk[4] * fac + add)] << 6) + | (remap[(int)(blk[6] * fac + add)] << 9); + blk += stride; + d = (remap[(int)(blk[0] * fac + add)] << 4) + | (remap[(int)(blk[2] * fac + add)] << 7) + | (remap[(int)(blk[4] * fac + add)] << 10) + | (remap[(int)(blk[6] * fac + add)] << 13); + + *(dest++) = maxv; + *(dest++) = minv; + *(dest++) = a & 0xff; + *(dest++) = (a >> 8) | (b & 0xf0); + *(dest++) = b >> 8; + *(dest++) = c & 0xff; + *(dest++) = (c >> 8) | (d & 0xf0); + *(dest++) = d >> 8; + + // Find the minimum and maximum green value in the block. + blk = src + 1; + minv = blk[0]; + maxv = blk[0]; + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + blk += stride; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + blk += stride; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + blk += stride; + minv = min(blk[0], minv); maxv = max(blk[0], maxv); + minv = min(blk[2], minv); maxv = max(blk[2], maxv); + minv = min(blk[4], minv); maxv = max(blk[4], maxv); + minv = min(blk[6], minv); maxv = max(blk[6], maxv); + + // Now calculate the index for each pixel. + if (maxv > minv) { + fac = 7.5f / (maxv - minv); + } else { + fac = 0; + } + add = -minv * fac; + blk = src + 1; + a = (remap[(int)(blk[0] * fac + add)]) + | (remap[(int)(blk[2] * fac + add)] << 3) + | (remap[(int)(blk[4] * fac + add)] << 6) + | (remap[(int)(blk[6] * fac + add)] << 9); + blk += stride; + b = (remap[(int)(blk[0] * fac + add)] << 4) + | (remap[(int)(blk[2] * fac + add)] << 7) + | (remap[(int)(blk[4] * fac + add)] << 10) + | (remap[(int)(blk[6] * fac + add)] << 13); + blk += stride; + c = (remap[(int)(blk[0] * fac + add)]) + | (remap[(int)(blk[2] * fac + add)] << 3) + | (remap[(int)(blk[4] * fac + add)] << 6) + | (remap[(int)(blk[6] * fac + add)] << 9); + blk += stride; + d = (remap[(int)(blk[0] * fac + add)] << 4) + | (remap[(int)(blk[2] * fac + add)] << 7) + | (remap[(int)(blk[4] * fac + add)] << 10) + | (remap[(int)(blk[6] * fac + add)] << 13); + + *(dest++) = maxv; + *(dest++) = minv; + *(dest++) = a & 0xff; + *(dest++) = (a >> 8) | (b & 0xf0); + *(dest++) = b >> 8; + *(dest++) = c & 0xff; + *(dest++) = (c >> 8) | (d & 0xf0); + *(dest++) = d >> 8; + + // Advance to the beginning of the next 4x4 block. + src += 8; + } + src += stride * 3; + } + Thread::consider_yield(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_uncompress_ram_image_bc4 +// Access: Protected, Static +// Description: Decompresses a RAM image compressed using BC4. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_uncompress_ram_image_bc4(const RamImage &compressed_image, + RamImage &uncompressed_image, + int x_size, int y_size, int num_pages) { + int x_blocks = (x_size >> 2); + int y_blocks = (y_size >> 2); + + for (int z = 0; z < num_pages; ++z) { + unsigned char *dest = uncompressed_image._image.p() + z * uncompressed_image._page_size; + unsigned const char *src = compressed_image._image.p() + z * compressed_image._page_size; + + // Unconvert one 4 x 4 block at a time. + PN_uint8 tbl[8]; + for (int y = 0; y < y_blocks; ++y) { + for (int x = 0; x < x_blocks; ++x) { + unsigned char *blk = dest; + tbl[0] = src[0]; + tbl[1] = src[1]; + if (tbl[0] > tbl[1]) { + tbl[2] = (tbl[0] * 6 + tbl[1] * 1) / 7.0f; + tbl[3] = (tbl[0] * 5 + tbl[1] * 2) / 7.0f; + tbl[4] = (tbl[0] * 4 + tbl[1] * 3) / 7.0f; + tbl[5] = (tbl[0] * 3 + tbl[1] * 4) / 7.0f; + tbl[6] = (tbl[0] * 2 + tbl[1] * 5) / 7.0f; + tbl[7] = (tbl[0] * 1 + tbl[1] * 6) / 7.0f; + } else { + tbl[2] = (tbl[0] * 4 + tbl[1] * 1) / 5.0f; + tbl[3] = (tbl[0] * 3 + tbl[1] * 2) / 5.0f; + tbl[4] = (tbl[0] * 2 + tbl[1] * 3) / 5.0f; + tbl[5] = (tbl[0] * 1 + tbl[1] * 4) / 5.0f; + tbl[6] = 0; + tbl[7] = 255; + } + int v = src[2] + (src[3] << 8) + (src[4] << 16); + blk[0] = tbl[v & 0x7]; + blk[1] = tbl[(v & 0x000038) >> 3]; + blk[2] = tbl[(v & 0x0001c0) >> 6]; + blk[3] = tbl[(v & 0x000e00) >> 9]; + blk += x_size; + blk[0] = tbl[(v & 0x007000) >> 12]; + blk[1] = tbl[(v & 0x038000) >> 15]; + blk[2] = tbl[(v & 0x1c0000) >> 18]; + blk[3] = tbl[(v & 0xe00000) >> 21]; + blk += x_size; + v = src[5] + (src[6] << 8) + (src[7] << 16); + blk[0] = tbl[v & 0x7]; + blk[1] = tbl[(v & 0x000038) >> 3]; + blk[2] = tbl[(v & 0x0001c0) >> 6]; + blk[3] = tbl[(v & 0x000e00) >> 9]; + blk += x_size; + blk[0] = tbl[(v & 0x007000) >> 12]; + blk[1] = tbl[(v & 0x038000) >> 15]; + blk[2] = tbl[(v & 0x1c0000) >> 18]; + blk[3] = tbl[(v & 0xe00000) >> 21]; + src += 8; + dest += 4; + } + dest += x_size * 3; + } + Thread::consider_yield(); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::do_uncompress_ram_image_bc5 +// Access: Protected, Static +// Description: Decompresses a RAM image compressed using BC5. +//////////////////////////////////////////////////////////////////// +void Texture:: +do_uncompress_ram_image_bc5(const RamImage &compressed_image, + RamImage &uncompressed_image, + int x_size, int y_size, int num_pages) { + int x_blocks = (x_size >> 2); + int y_blocks = (y_size >> 2); + int stride = x_size * 2; + + for (int z = 0; z < num_pages; ++z) { + unsigned char *dest = uncompressed_image._image.p() + z * uncompressed_image._page_size; + unsigned const char *src = compressed_image._image.p() + z * compressed_image._page_size; + + // Unconvert one 4 x 4 block at a time. + PN_uint8 red[8]; + PN_uint8 grn[8]; + for (int y = 0; y < y_blocks; ++y) { + for (int x = 0; x < x_blocks; ++x) { + unsigned char *blk = dest; + red[0] = src[0]; + red[1] = src[1]; + if (red[0] > red[1]) { + red[2] = (red[0] * 6 + red[1] * 1) / 7.0f; + red[3] = (red[0] * 5 + red[1] * 2) / 7.0f; + red[4] = (red[0] * 4 + red[1] * 3) / 7.0f; + red[5] = (red[0] * 3 + red[1] * 4) / 7.0f; + red[6] = (red[0] * 2 + red[1] * 5) / 7.0f; + red[7] = (red[0] * 1 + red[1] * 6) / 7.0f; + } else { + red[2] = (red[0] * 4 + red[1] * 1) / 5.0f; + red[3] = (red[0] * 3 + red[1] * 2) / 5.0f; + red[4] = (red[0] * 2 + red[1] * 3) / 5.0f; + red[5] = (red[0] * 1 + red[1] * 4) / 5.0f; + red[6] = 0; + red[7] = 255; + } + grn[0] = src[8]; + grn[1] = src[9]; + if (grn[0] > grn[1]) { + grn[2] = (grn[0] * 6 + grn[1] * 1) / 7.0f; + grn[3] = (grn[0] * 5 + grn[1] * 2) / 7.0f; + grn[4] = (grn[0] * 4 + grn[1] * 3) / 7.0f; + grn[5] = (grn[0] * 3 + grn[1] * 4) / 7.0f; + grn[6] = (grn[0] * 2 + grn[1] * 5) / 7.0f; + grn[7] = (grn[0] * 1 + grn[1] * 6) / 7.0f; + } else { + grn[2] = (grn[0] * 4 + grn[1] * 1) / 5.0f; + grn[3] = (grn[0] * 3 + grn[1] * 2) / 5.0f; + grn[4] = (grn[0] * 2 + grn[1] * 3) / 5.0f; + grn[5] = (grn[0] * 1 + grn[1] * 4) / 5.0f; + grn[6] = 0; + grn[7] = 255; + } + int r = src[2] + (src[3] << 8) + (src[4] << 16); + int g = src[10] + (src[11] << 8) + (src[12] << 16); + blk[0] = red[r & 0x7]; + blk[1] = grn[g & 0x7]; + blk[2] = red[(r & 0x000038) >> 3]; + blk[3] = grn[(g & 0x000038) >> 3]; + blk[4] = red[(r & 0x0001c0) >> 6]; + blk[5] = grn[(g & 0x0001c0) >> 6]; + blk[6] = red[(r & 0x000e00) >> 9]; + blk[7] = grn[(g & 0x000e00) >> 9]; + blk += stride; + blk[0] = red[(r & 0x007000) >> 12]; + blk[1] = grn[(g & 0x007000) >> 12]; + blk[2] = red[(r & 0x038000) >> 15]; + blk[3] = grn[(g & 0x038000) >> 15]; + blk[4] = red[(r & 0x1c0000) >> 18]; + blk[5] = grn[(g & 0x1c0000) >> 18]; + blk[6] = red[(r & 0xe00000) >> 21]; + blk[7] = grn[(g & 0xe00000) >> 21]; + blk += stride; + r = src[5] + (src[6] << 8) + (src[7] << 16); + g = src[13] + (src[14] << 8) + (src[15] << 16); + blk[0] = red[r & 0x7]; + blk[1] = grn[g & 0x7]; + blk[2] = red[(r & 0x000038) >> 3]; + blk[3] = grn[(g & 0x000038) >> 3]; + blk[4] = red[(r & 0x0001c0) >> 6]; + blk[5] = grn[(g & 0x0001c0) >> 6]; + blk[6] = red[(r & 0x000e00) >> 9]; + blk[7] = grn[(g & 0x000e00) >> 9]; + blk += stride; + blk[0] = red[(r & 0x007000) >> 12]; + blk[1] = grn[(g & 0x007000) >> 12]; + blk[2] = red[(r & 0x038000) >> 15]; + blk[3] = grn[(g & 0x038000) >> 15]; + blk[4] = red[(r & 0x1c0000) >> 18]; + blk[5] = grn[(g & 0x1c0000) >> 18]; + blk[6] = red[(r & 0xe00000) >> 21]; + blk[7] = grn[(g & 0xe00000) >> 21]; + src += 16; + dest += 8; + } + dest += stride * 3; + } + Thread::consider_yield(); + } +} + //////////////////////////////////////////////////////////////////// // Function: Texture::do_has_all_ram_mipmap_images // Access: Protected @@ -5254,6 +6103,7 @@ do_set_format(CData *cdata, Texture::Format format) { case F_sluminance_alpha: case F_rg32: case F_rg8i: + case F_rg: cdata->_num_components = 2; break; @@ -5267,6 +6117,7 @@ do_set_format(CData *cdata, Texture::Format format) { case F_rgb32: case F_rgb8i: case F_r11_g11_b10: + case F_rgb9_e5: cdata->_num_components = 3; break; @@ -5280,6 +6131,7 @@ do_set_format(CData *cdata, Texture::Format format) { case F_rgba32: case F_srgb_alpha: case F_rgba8i: + case F_rgb10_a2: cdata->_num_components = 4; break; } @@ -5302,6 +6154,7 @@ do_set_component_type(CData *cdata, Texture::ComponentType component_type) { case T_unsigned_short: case T_short: + case T_half_float: cdata->_component_width = 2; break; @@ -6542,6 +7395,64 @@ read_dds_level_rgba8(Texture *tex, CData *cdata, const DDSHeader &header, int n, return image; } +//////////////////////////////////////////////////////////////////// +// Function: Texture::read_dds_level_abgr16 +// Access: Private, Static +// Description: Called by read_dds for a DDS file in ABGR16 format. +//////////////////////////////////////////////////////////////////// +PTA_uchar Texture:: +read_dds_level_abgr16(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { + // This is laid out in order R, G, B, A. + int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); + int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); + + size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n); + size_t row_bytes = x_size * 8; + PTA_uchar image = PTA_uchar::empty_array(size); + for (int y = y_size - 1; y >= 0; --y) { + unsigned char *p = image.p() + y * row_bytes; + in.read((char *)p, row_bytes); + + PN_uint16 *pw = (PN_uint16 *)p; + for (int x = 0; x < x_size; ++x) { + swap(pw[0], pw[2]); + pw += 4; + } + nassertr((unsigned char *)pw <= image.p() + size, PTA_uchar()); + } + + return image; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::read_dds_level_abgr32 +// Access: Private, Static +// Description: Called by read_dds for a DDS file in ABGR32 format. +//////////////////////////////////////////////////////////////////// +PTA_uchar Texture:: +read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { + // This is laid out in order R, G, B, A. + int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); + int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); + + size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n); + size_t row_bytes = x_size * 16; + PTA_uchar image = PTA_uchar::empty_array(size); + for (int y = y_size - 1; y >= 0; --y) { + unsigned char *p = image.p() + y * row_bytes; + in.read((char *)p, row_bytes); + + PN_uint32 *pw = (PN_uint32 *)p; + for (int x = 0; x < x_size; ++x) { + swap(pw[0], pw[2]); + pw += 4; + } + nassertr((unsigned char *)pw <= image.p() + size, PTA_uchar()); + } + + return image; +} + //////////////////////////////////////////////////////////////////// // Function: Texture::read_dds_level_generic_uncompressed // Access: Private, Static @@ -6731,12 +7642,12 @@ read_dds_level_luminance_uncompressed(Texture *tex, CData *cdata, const DDSHeade } //////////////////////////////////////////////////////////////////// -// Function: Texture::read_dds_level_dxt1 +// Function: Texture::read_dds_level_bc1 // Access: Private, Static // Description: Called by read_dds for DXT1 file format. //////////////////////////////////////////////////////////////////// PTA_uchar Texture:: -read_dds_level_dxt1(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { +read_dds_level_bc1(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); @@ -6802,12 +7713,12 @@ read_dds_level_dxt1(Texture *tex, CData *cdata, const DDSHeader &header, int n, } //////////////////////////////////////////////////////////////////// -// Function: Texture::read_dds_level_dxt23 +// Function: Texture::read_dds_level_bc2 // Access: Private, Static // Description: Called by read_dds for DXT2 or DXT3 file format. //////////////////////////////////////////////////////////////////// PTA_uchar Texture:: -read_dds_level_dxt23(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { +read_dds_level_bc2(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); @@ -6891,12 +7802,12 @@ read_dds_level_dxt23(Texture *tex, CData *cdata, const DDSHeader &header, int n, } //////////////////////////////////////////////////////////////////// -// Function: Texture::read_dds_level_dxt45 +// Function: Texture::read_dds_level_bc3 // Access: Private, Static // Description: Called by read_dds for DXT4 or DXT5 file format. //////////////////////////////////////////////////////////////////// PTA_uchar Texture:: -read_dds_level_dxt45(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { +read_dds_level_bc3(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); @@ -6995,6 +7906,173 @@ read_dds_level_dxt45(Texture *tex, CData *cdata, const DDSHeader &header, int n, return image; } +//////////////////////////////////////////////////////////////////// +// Function: Texture::read_dds_level_bc4 +// Access: Private, Static +// Description: Called by read_dds for ATI1 compression. +//////////////////////////////////////////////////////////////////// +PTA_uchar Texture:: +read_dds_level_bc4(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { + int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); + int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); + + static const int div = 4; + static const int block_bytes = 8; + + // The ATI1 (BC4) format uses the same compression mechanism as the + // alpha channel of DXT5. + int num_cols = max(div, x_size) / div; + int num_rows = max(div, y_size) / div; + int row_length = num_cols * block_bytes; + int linear_size = row_length * num_rows; + + if (n == 0) { + if (header.dds_flags & DDSD_LINEARSIZE) { + nassertr(linear_size == (int)header.pitch, PTA_uchar()); + } + } + + PTA_uchar image = PTA_uchar::empty_array(linear_size); + + if (y_size >= 4) { + // We have to flip the image as we read it, because of DirectX's + // inverted sense of up. That means we (a) reverse the order of the + // rows of blocks . . . + for (int ri = num_rows - 1; ri >= 0; --ri) { + unsigned char *p = image.p() + row_length * ri; + in.read((char *)p, row_length); + + for (int ci = 0; ci < num_cols; ++ci) { + // . . . and (b) within each block, we reverse the 4 individual + // rows of 4 pixels. + // The block is one 16-bit word of reference values, followed by + // six words of pixel values, in 12-bit rows. Tricky to invert. + unsigned char p2 = p[2]; + unsigned char p3 = p[3]; + unsigned char p4 = p[4]; + unsigned char p5 = p[5]; + unsigned char p6 = p[6]; + unsigned char p7 = p[7]; + + p[2] = ((p7 & 0xf) << 4) | ((p6 & 0xf0) >> 4); + p[3] = ((p5 & 0xf) << 4) | ((p7 & 0xf0) >> 4); + p[4] = ((p6 & 0xf) << 4) | ((p5 & 0xf0) >> 4); + p[5] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4); + p[6] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4); + p[7] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4); + + p += block_bytes; + } + } + + } else if (y_size >= 2) { + // To invert a two-pixel high image, we just flip two rows within a cell. + unsigned char *p = image.p(); + in.read((char *)p, row_length); + + for (int ci = 0; ci < num_cols; ++ci) { + unsigned char p2 = p[2]; + unsigned char p3 = p[3]; + unsigned char p4 = p[4]; + + p[2] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4); + p[3] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4); + p[4] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4); + + p += block_bytes; + } + + } else if (y_size >= 1) { + // No need to invert a one-pixel-high image. + unsigned char *p = image.p(); + in.read((char *)p, row_length); + } + + return image; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::read_dds_level_bc5 +// Access: Private, Static +// Description: Called by read_dds for ATI2 compression. +//////////////////////////////////////////////////////////////////// +PTA_uchar Texture:: +read_dds_level_bc5(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { + int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); + int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); + + // The ATI2 (BC5) format uses the same compression mechanism as the + // ATI1 (BC4) format, but doubles the channels. + int num_cols = max(4, x_size) / 2; + int num_rows = max(4, y_size) / 4; + int row_length = num_cols * 8; + int linear_size = row_length * num_rows; + + if (n == 0) { + if (header.dds_flags & DDSD_LINEARSIZE) { + nassertr(linear_size == (int)header.pitch, PTA_uchar()); + } + } + + PTA_uchar image = PTA_uchar::empty_array(linear_size); + + if (y_size >= 4) { + // We have to flip the image as we read it, because of DirectX's + // inverted sense of up. That means we (a) reverse the order of the + // rows of blocks . . . + for (int ri = num_rows - 1; ri >= 0; --ri) { + unsigned char *p = image.p() + row_length * ri; + in.read((char *)p, row_length); + + for (int ci = 0; ci < num_cols; ++ci) { + // . . . and (b) within each block, we reverse the 4 individual + // rows of 4 pixels. + // The block is one 16-bit word of reference values, followed by + // six words of pixel values, in 12-bit rows. Tricky to invert. + unsigned char p2 = p[2]; + unsigned char p3 = p[3]; + unsigned char p4 = p[4]; + unsigned char p5 = p[5]; + unsigned char p6 = p[6]; + unsigned char p7 = p[7]; + + p[2] = ((p7 & 0xf) << 4) | ((p6 & 0xf0) >> 4); + p[3] = ((p5 & 0xf) << 4) | ((p7 & 0xf0) >> 4); + p[4] = ((p6 & 0xf) << 4) | ((p5 & 0xf0) >> 4); + p[5] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4); + p[6] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4); + p[7] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4); + + p += 8; + } + } + + } else if (y_size >= 2) { + // To invert a two-pixel high image, we just flip two rows within a cell. + unsigned char *p = image.p(); + in.read((char *)p, row_length); + + for (int ci = 0; ci < num_cols; ++ci) { + unsigned char p2 = p[2]; + unsigned char p3 = p[3]; + unsigned char p4 = p[4]; + + p[2] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4); + p[3] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4); + p[4] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4); + + p += 8; + } + + } else if (y_size >= 1) { + // No need to invert a one-pixel-high image. + unsigned char *p = image.p(); + in.read((char *)p, row_length); + } + + return image; +} + //////////////////////////////////////////////////////////////////// // Function: Texture::clear_prepared // Access: Private @@ -7695,10 +8773,6 @@ filter_3d_float(unsigned char *&p, const unsigned char *&q, bool Texture:: do_squish(CData *cdata, Texture::CompressionMode compression, int squish_flags) { #ifdef HAVE_SQUISH - if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) { - return false; - } - if (!do_has_all_ram_mipmap_images(cdata)) { // If we're about to compress the RAM image, we should ensure that // we have all of the mipmap levels first. @@ -7793,9 +8867,6 @@ do_squish(CData *cdata, Texture::CompressionMode compression, int squish_flags) bool Texture:: do_unsquish(CData *cdata, int squish_flags) { #ifdef HAVE_SQUISH - if (cdata->_ram_images.empty()) { - return false; - } RamImages uncompressed_ram_images; uncompressed_ram_images.reserve(cdata->_ram_images.size()); for (size_t n = 0; n < cdata->_ram_images.size(); ++n) { diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 0b1e2546a1..a8a9afb1ba 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -95,6 +95,7 @@ PUBLISHED: T_int, T_byte, T_short, + T_half_float, }; enum Format { @@ -160,7 +161,10 @@ PUBLISHED: F_rgba8i, // 8 integer bits per R,G,B,A channel F_r11_g11_b10, // unsigned floating-point, 11 Red, 11 Green, 10 Blue Bits + F_rgb9_e5, + F_rgb10_a2, + F_rg, }; // Deprecated. See SamplerState.FilterType. @@ -201,13 +205,14 @@ PUBLISHED: // GSG::get_supports_compressed_texture_format() to query the // available compression modes for a particular GSG. CM_fxt1, - CM_dxt1, - CM_dxt2, - CM_dxt3, - CM_dxt4, - CM_dxt5, + CM_dxt1, // BC1: RGB with optional binary alpha. + CM_dxt2, // Like DXT3, but assumes premultiplied alpha + CM_dxt3, // BC2: RGB with uncompressed 4-bit alpha. + CM_dxt4, // Like DXT5, but assumes premultiplied alpha + CM_dxt5, // BC3: RGB with separately compressed 8-bit alpha. CM_pvr1_2bpp, CM_pvr1_4bpp, + CM_rgtc, // BC4/BC5: 1 or 2 channels, individually compressed. }; enum QualityLevel { @@ -568,6 +573,7 @@ public: protected: class CData; + class RamImage; virtual void reconsider_dirty(); @@ -633,6 +639,15 @@ protected: QualityLevel quality_level, GraphicsStateGuardianBase *gsg); bool do_uncompress_ram_image(CData *cdata); + + static void do_compress_ram_image_bc4(const RamImage &src, RamImage &dest, + int x_size, int y_size, int z_size); + static void do_compress_ram_image_bc5(const RamImage &src, RamImage &dest, + int x_size, int y_size, int z_size); + static void do_uncompress_ram_image_bc4(const RamImage &src, RamImage &dest, + int x_size, int y_size, int z_size); + static void do_uncompress_ram_image_bc5(const RamImage &src, RamImage &dest, + int x_size, int y_size, int z_size); bool do_has_all_ram_mipmap_images(const CData *cdata) const; bool do_reconsider_z_size(CData *cdata, int z, const LoaderOptions &options); @@ -738,21 +753,31 @@ private: int n, istream &in); static PTA_uchar read_dds_level_rgba8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in); + static PTA_uchar read_dds_level_abgr16(Texture *tex, CData *cdata, const DDSHeader &header, + int n, istream &in); + static PTA_uchar read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, + int n, istream &in); static PTA_uchar read_dds_level_generic_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in); static PTA_uchar read_dds_level_luminance_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in); - static PTA_uchar read_dds_level_dxt1(Texture *tex, CData *cdata, - const DDSHeader &header, - int n, istream &in); - static PTA_uchar read_dds_level_dxt23(Texture *tex, CData *cdata, - const DDSHeader &header, - int n, istream &in); - static PTA_uchar read_dds_level_dxt45(Texture *tex, CData *cdata, - const DDSHeader &header, - int n, istream &in); + static PTA_uchar read_dds_level_bc1(Texture *tex, CData *cdata, + const DDSHeader &header, + int n, istream &in); + static PTA_uchar read_dds_level_bc2(Texture *tex, CData *cdata, + const DDSHeader &header, + int n, istream &in); + static PTA_uchar read_dds_level_bc3(Texture *tex, CData *cdata, + const DDSHeader &header, + int n, istream &in); + static PTA_uchar read_dds_level_bc4(Texture *tex, CData *cdata, + const DDSHeader &header, + int n, istream &in); + static PTA_uchar read_dds_level_bc5(Texture *tex, CData *cdata, + const DDSHeader &header, + int n, istream &in); void clear_prepared(int view, PreparedGraphicsObjects *prepared_objects);