@@ -449,7 +449,51 @@ qboolean AssetCooking_CompressKTX2(byte* input_data, size_t input_size,
449449#ifdef USE_KTX2
450450 Com_Printf ("AssetCooking_CompressKTX2: Starting KTX2 compression\n" );
451451
452- // Basic KTX2 header structure (simplified)
452+ // Get texture dimensions
453+ uint32_t width = options ? options -> width : 256 ;
454+ uint32_t height = options ? options -> height : 256 ;
455+ uint32_t quality = options ? options -> quality : COOK_QUALITY_MEDIUM ;
456+
457+ // Determine compression format based on quality and options
458+ qboolean useBasisU = (quality >= COOK_QUALITY_HIGH ) ||
459+ (options && options -> use_basisu );
460+
461+ // Generate mipmaps if requested
462+ uint32_t levelCount = 1 ;
463+ if (options && options -> generate_mipmaps ) {
464+ levelCount = 1 + (uint32_t )floor (log2 (MAX (width , height )));
465+ }
466+
467+ // Calculate compressed data size
468+ size_t compressedSize = 0 ;
469+ byte * compressedData = NULL ;
470+
471+ if (useBasisU ) {
472+ // Use BasisU compression
473+ if (!AssetCooking_CompressBasisU (input_data , input_size , & compressedData ,
474+ & compressedSize , options )) {
475+ Com_Printf ("AssetCooking_CompressKTX2: BasisU compression failed, falling back to uncompressed\n" );
476+ useBasisU = qfalse ;
477+ }
478+ }
479+
480+ if (!useBasisU ) {
481+ // Use standard compression or uncompressed
482+ uint32_t format = 37 ; // VK_FORMAT_R8G8B8A8_UNORM (default)
483+
484+ if (quality >= COOK_QUALITY_HIGH && options && options -> allow_compression ) {
485+ // Use BC7 for high quality
486+ format = 143 ; // VK_FORMAT_BC7_UNORM_BLOCK
487+ // In a real implementation, this would compress the data
488+ }
489+
490+ compressedSize = input_size ;
491+ compressedData = (byte * )malloc (compressedSize );
492+ if (!compressedData ) return qfalse ;
493+ memcpy (compressedData , input_data , compressedSize );
494+ }
495+
496+ // Create KTX2 file structure
453497 typedef struct {
454498 uint32_t magic [2 ]; // KTX2 magic number
455499 uint32_t format ; // Vulkan format
@@ -463,40 +507,76 @@ qboolean AssetCooking_CompressKTX2(byte* input_data, size_t input_size,
463507 uint32_t supercompressionScheme ; // Compression scheme
464508 } ktx2_header_t ;
465509
466- // For now, create a basic KTX2 file with uncompressed data
467- // In a full implementation, this would use the KTX-Software library
510+ typedef struct {
511+ uint32_t dfdByteOffset ; // Data format descriptor offset
512+ uint32_t dfdByteLength ; // Data format descriptor length
513+ uint32_t kvdByteOffset ; // Key/value data offset
514+ uint32_t kvdByteLength ; // Key/value data length
515+ uint64_t sgdByteOffset ; // Supercompression global data offset
516+ uint64_t sgdByteLength ; // Supercompression global data length
517+ } ktx2_index_t ;
468518
469- // Assume input is RGBA8 data
470- uint32_t width = options ? options -> width : 256 ;
471- uint32_t height = options ? options -> height : 256 ;
519+ typedef struct {
520+ uint64_t byteOffset ; // Offset from start of file
521+ uint64_t byteLength ; // Length of data
522+ uint64_t uncompressedByteLength ; // Uncompressed length
523+ } ktx2_level_index_t ;
472524
473- size_t ktx2_size = sizeof (ktx2_header_t ) + input_size ;
525+ size_t ktx2_size = sizeof (ktx2_header_t ) + sizeof (ktx2_index_t ) +
526+ levelCount * sizeof (ktx2_level_index_t ) + compressedSize ;
474527
475528 * output_data = (byte * )malloc (ktx2_size );
476- if (!* output_data ) return qfalse ;
529+ if (!* output_data ) {
530+ free (compressedData );
531+ return qfalse ;
532+ }
477533
478534 ktx2_header_t * header = (ktx2_header_t * )* output_data ;
479535
480536 // KTX2 magic: «KTX 2»
481537 header -> magic [0 ] = 0x58544BAB ; // «KTX
482538 header -> magic [1 ] = 0x00000020 ; // 2»
483539
484- header -> format = 37 ; // VK_FORMAT_R8G8B8A8_UNORM
540+ header -> format = useBasisU ? 0xFFFFFFFF : 37 ; // Custom format for BasisU or RGBA8
485541 header -> typeSize = 1 ;
486542 header -> pixelWidth = width ;
487543 header -> pixelHeight = height ;
488544 header -> pixelDepth = 0 ;
489545 header -> layerCount = 0 ;
490546 header -> faceCount = 1 ;
491- header -> levelCount = 1 ;
492- header -> supercompressionScheme = 0 ; // No supercompression
547+ header -> levelCount = levelCount ;
548+ header -> supercompressionScheme = useBasisU ? 1 : 0 ; // BasisLZ for BasisU
549+
550+ // Write index
551+ ktx2_index_t * index = (ktx2_index_t * )(* output_data + sizeof (ktx2_header_t ));
552+ index -> dfdByteOffset = 0 ;
553+ index -> dfdByteLength = 0 ;
554+ index -> kvdByteOffset = 0 ;
555+ index -> kvdByteLength = 0 ;
556+ index -> sgdByteOffset = 0 ;
557+ index -> sgdByteLength = 0 ;
558+
559+ // Write level indices
560+ ktx2_level_index_t * levelIndices = (ktx2_level_index_t * )(* output_data + sizeof (ktx2_header_t ) + sizeof (ktx2_index_t ));
561+ size_t dataOffset = sizeof (ktx2_header_t ) + sizeof (ktx2_index_t ) + levelCount * sizeof (ktx2_level_index_t );
562+
563+ for (uint32_t i = 0 ; i < levelCount ; i ++ ) {
564+ levelIndices [i ].byteOffset = dataOffset ;
565+ levelIndices [i ].byteLength = compressedSize / levelCount ; // Simplified - equal size per level
566+ levelIndices [i ].uncompressedByteLength = (width >> i ) * (height >> i ) * 4 ; // RGBA8
567+ dataOffset += levelIndices [i ].byteLength ;
568+ }
569+
570+ // Copy compressed texture data
571+ memcpy (* output_data + sizeof (ktx2_header_t ) + sizeof (ktx2_index_t ) + levelCount * sizeof (ktx2_level_index_t ),
572+ compressedData , compressedSize );
493573
494- // Copy texture data after header
495- memcpy (* output_data + sizeof (ktx2_header_t ), input_data , input_size );
496574 * output_size = ktx2_size ;
497575
498- Com_Printf ("AssetCooking_CompressKTX2: Created KTX2 file (%dx%d, %zu bytes)\n" ,
499- width , height , * output_size );
576+ free (compressedData );
577+
578+ Com_Printf ("AssetCooking_CompressKTX2: Created KTX2 file (%dx%d, %d levels, %s, %zu bytes)\n" ,
579+ width , height , levelCount , useBasisU ? "BasisU" : "RGBA8" , * output_size );
500580 return qtrue ;
501581
502582#else
@@ -518,7 +598,80 @@ qboolean AssetCooking_CompressBasisU(byte* input_data, size_t input_size,
518598#ifdef USE_BASISU
519599 Com_Printf ("AssetCooking_CompressBasisU: Starting BasisU compression\n" );
520600
521- // Basic BasisU header structure (simplified)
601+ uint32_t width = options ? options -> width : 256 ;
602+ uint32_t height = options ? options -> height : 256 ;
603+ uint32_t quality = options ? options -> quality : COOK_QUALITY_MEDIUM ;
604+
605+ // Calculate number of 4x4 blocks
606+ uint32_t blocksX = (width + 3 ) / 4 ;
607+ uint32_t blocksY = (height + 3 ) / 4 ;
608+ uint32_t blockCount = blocksX * blocksY ;
609+
610+ // Each block is compressed to 16 bytes in ETC1S format (simplified)
611+ // In a real implementation, this would be much more complex
612+ size_t compressedSize = blockCount * 16 ; // 16 bytes per block for ETC1S
613+
614+ // Create compressed data buffer
615+ byte * compressedData = (byte * )malloc (compressedSize );
616+ if (!compressedData ) return qfalse ;
617+
618+ // Simple ETC1S-like compression (placeholder)
619+ // In a real implementation, this would use the BasisU encoder
620+ memset (compressedData , 0 , compressedSize );
621+
622+ // For each 4x4 block in the input texture
623+ const uint32_t * rgbaPixels = (const uint32_t * )input_data ;
624+ uint8_t * compressedBlocks = compressedData ;
625+
626+ for (uint32_t by = 0 ; by < blocksY ; by ++ ) {
627+ for (uint32_t bx = 0 ; bx < blocksX ; bx ++ ) {
628+ // Extract 4x4 block from input
629+ uint32_t blockPixels [16 ];
630+ for (uint32_t y = 0 ; y < 4 ; y ++ ) {
631+ for (uint32_t x = 0 ; x < 4 ; x ++ ) {
632+ uint32_t srcX = bx * 4 + x ;
633+ uint32_t srcY = by * 4 + y ;
634+
635+ if (srcX < width && srcY < height ) {
636+ blockPixels [y * 4 + x ] = rgbaPixels [srcY * width + srcX ];
637+ } else {
638+ blockPixels [y * 4 + x ] = 0 ; // Padding
639+ }
640+ }
641+ }
642+
643+ // Compress 4x4 block to 16 bytes (simplified ETC1S encoding)
644+ uint8_t * blockData = compressedBlocks + (by * blocksX + bx ) * 16 ;
645+
646+ // Simple color encoding - find min/max colors
647+ uint32_t minColor = 0xFFFFFFFF ;
648+ uint32_t maxColor = 0x00000000 ;
649+
650+ for (int i = 0 ; i < 16 ; i ++ ) {
651+ uint32_t color = blockPixels [i ];
652+ if (color < minColor ) minColor = color ;
653+ if (color > maxColor ) maxColor = color ;
654+ }
655+
656+ // Encode endpoints (simplified)
657+ blockData [0 ] = (minColor >> 16 ) & 0xFF ; // R0
658+ blockData [1 ] = (minColor >> 8 ) & 0xFF ; // G0
659+ blockData [2 ] = minColor & 0xFF ; // B0
660+ blockData [3 ] = (maxColor >> 16 ) & 0xFF ; // R1
661+ blockData [4 ] = (maxColor >> 8 ) & 0xFF ; // G1
662+ blockData [5 ] = maxColor & 0xFF ; // B1
663+
664+ // Simple selectors - alternate between endpoints
665+ for (int i = 0 ; i < 8 ; i ++ ) {
666+ blockData [6 + i ] = (i % 2 == 0 ) ? 0xAA : 0x55 ; // Alternating pattern
667+ }
668+
669+ // Fill remaining bytes with zeros
670+ memset (blockData + 14 , 0 , 2 );
671+ }
672+ }
673+
674+ // Create BasisU file header
522675 typedef struct {
523676 uint32_t magic ; // 'b' 'a' 's' 'i' 's' 0 0 0
524677 uint32_t version ; // Version number
@@ -528,12 +681,12 @@ qboolean AssetCooking_CompressBasisU(byte* input_data, size_t input_size,
528681 uint16_t total_slices ; // Total number of slices
529682 uint16_t slice_info_size ; // Size of slice info
530683 uint8_t flags ; // Compression flags
531- uint8_t tex_format ; // Texture format
684+ uint8_t tex_format ; // Texture format (0x0D = ETC1S)
532685 uint16_t us_per_frame ; // Microseconds per frame
533686 uint16_t total_images ; // Total images
534687 uint8_t userdata0 ; // User data
535688 uint8_t userdata1 ; // User data
536- uint32_t tex_type ; // Texture type
689+ uint32_t tex_type ; // Texture type (0 = 2D)
537690 uint32_t orig_width ; // Original width
538691 uint32_t orig_height ; // Original height
539692 uint32_t num_blocks_x ; // Number of 4x4 blocks in X
@@ -546,51 +699,49 @@ qboolean AssetCooking_CompressBasisU(byte* input_data, size_t input_size,
546699 uint32_t ext_data_ofs ; // Extended data offset
547700 } basis_header_t ;
548701
549- // For now, create a basic BasisU file structure
550- // In a full implementation, this would use the Basis Universal library
551-
552- uint32_t width = options ? options -> width : 256 ;
553- uint32_t height = options ? options -> height : 256 ;
554-
555- size_t basis_size = sizeof (basis_header_t ) + input_size ;
556-
557- * output_data = (byte * )malloc (basis_size );
558- if (!* output_data ) return qfalse ;
702+ size_t totalSize = sizeof (basis_header_t ) + compressedSize ;
703+ * output_data = (byte * )malloc (totalSize );
704+ if (!* output_data ) {
705+ free (compressedData );
706+ return qfalse ;
707+ }
559708
560709 basis_header_t * header = (basis_header_t * )* output_data ;
561710
562711 // BasisU magic: 'b' 'a' 's' 'i' 's' 0 0 0
563- header -> magic = 0x00697361 ; // 'asis' (little endian)
564- header -> version = 0x10 ; // Version 1.16
712+ header -> magic = 0x00697361 ; // 'asis' (little endian)
713+ header -> version = 0x10 ; // Version 1.16
565714 header -> header_size = sizeof (basis_header_t );
566- header -> header_crc16 = 0 ; // Would compute CRC16 in full implementation
567- header -> data_size = input_size ;
715+ header -> header_crc16 = 0 ; // Would compute CRC16 in full implementation
716+ header -> data_size = compressedSize ;
568717 header -> total_slices = 1 ;
569718 header -> slice_info_size = 0 ;
570- header -> flags = 0x02 ; // Has alpha slices
571- header -> tex_format = 0xFF ; // Invalid format (placeholder)
719+ header -> flags = 0x02 ; // Has alpha slices
720+ header -> tex_format = 0x0D ; // ETC1S format
572721 header -> us_per_frame = 0 ;
573722 header -> total_images = 1 ;
574723 header -> userdata0 = 0 ;
575724 header -> userdata1 = 0 ;
576- header -> tex_type = 0 ; // 2D texture
725+ header -> tex_type = 0 ; // 2D texture
577726 header -> orig_width = width ;
578727 header -> orig_height = height ;
579- header -> num_blocks_x = ( width + 3 ) / 4 ;
580- header -> num_blocks_y = ( height + 3 ) / 4 ;
728+ header -> num_blocks_x = blocksX ;
729+ header -> num_blocks_y = blocksY ;
581730 header -> num_blocks_z = 1 ;
582- header -> total_endpoints = 0 ;
731+ header -> total_endpoints = blockCount * 2 ; // 2 endpoints per block
583732 header -> endpoint_palette_ofs = 0 ;
584733 header -> selector_palette_ofs = 0 ;
585734 header -> tables_ofs = 0 ;
586735 header -> ext_data_ofs = 0 ;
587736
588- // Copy texture data after header
589- memcpy (* output_data + sizeof (basis_header_t ), input_data , input_size );
590- * output_size = basis_size ;
737+ // Copy compressed data after header
738+ memcpy (* output_data + sizeof (basis_header_t ), compressedData , compressedSize );
739+ * output_size = totalSize ;
740+
741+ free (compressedData );
591742
592- Com_Printf ("AssetCooking_CompressBasisU: Created BasisU file (%dx%d, %zu bytes)\n" ,
593- width , height , * output_size );
743+ Com_Printf ("AssetCooking_CompressBasisU: Created BasisU file (%dx%d, %dx%d blocks, % zu bytes)\n" ,
744+ width , height , blocksX , blocksY , * output_size );
594745 return qtrue ;
595746
596747#else
0 commit comments