From Jason Daly, "'ve been busy working on the Source engine plugins. There are several contributions in this submission:
osgDB/FileUtils.cpp: Needed this extra code to allow a true case-insensitive search. This is because the HL2 map and model files are often sloppy with case. For example, the file might look for materials/models/alyx/alyx_sheet.vtf, but the file is actually in materials/Models/Alyx/alyx_sheet.vtf. In case-insensitive mode, the new code recursively disassembles the path and checks each path element without regard to case. In case-sensitive mode, the code behaves exactly as it used to. The new code is also mostly skipped on Windows because of the case-insensitive file system. Previously, I did all of this with custom search code in the .bsp plugin, but this allows the user to tailor the search using OSGFILEPATH. There are some instructions in the plugins' README files about this. osgPlugins/mdl: This is a new plug-in for Half-Life 2 models (as opposed to maps). This allows you to load Source models individually, as well as allowing the .bsp plugin to load models (props) that are embedded into maps. Mdl files can contain simple object (crates, barrels, bottles), as well as fully articulated characters with skeletal animations. Currently, it can load the simple objects. It can also load the characters, but it can't load the skeletons or animations. osgPlugins/bsp: This contains all of the changes needed to load props along with the basic map geometry. There are also several bugs fixed. osgPlugins/vtf: This is the loader for Valve's texture format. Previously, we had agreed to put this in with the bsp plugin, but I didn't think of the .mdl plugin at that time. It's conceivable that a user might want to load models individually (not as part of a map), so the vtf reader does have to be separate. I also fixed a rather significant bug. I tested all of this code on RHEL 5.2 (32-bit), and Fedora 9 (64-bit). I'll be testing on Windows soon. I also attached a simple .mdl file, along with it's associated files and textures. Just extract the tarball into it's own directory, set your OSGFILEPATH to point at that directory, and load the model like this: osgviewer models/props_junk/gascan001a.mdl"
This commit is contained in:
783
src/osgPlugins/vtf/ReaderWriterVTF.cpp
Normal file
783
src/osgPlugins/vtf/ReaderWriterVTF.cpp
Normal file
@@ -0,0 +1,783 @@
|
||||
/**********************************************************************
|
||||
*
|
||||
* FILE: ReaderWriterVTF.cpp
|
||||
*
|
||||
* DESCRIPTION: Class for reading a Valve Texture Format (VTF) file
|
||||
* into an osg::Image.
|
||||
*
|
||||
* Borrows heavily from the DDS plugin for OSG, as well
|
||||
* as the Valve Source SDK
|
||||
*
|
||||
* CREATED BY: Jason Daly (jdaly@ist.ucf.edu)
|
||||
*
|
||||
* HISTORY: Created 27.10.2008
|
||||
*
|
||||
**********************************************************************/
|
||||
|
||||
#include <osg/Texture>
|
||||
#include <osg/Notify>
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/FileNameUtils>
|
||||
#include <osgDB/FileUtils>
|
||||
|
||||
#include <iomanip>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
enum VTFFlags
|
||||
{
|
||||
VTF_FLAGS_POINTSAMPLE = 0x00000001,
|
||||
VTF_FLAGS_TRILINEAR = 0x00000002,
|
||||
VTF_FLAGS_CLAMP_S = 0x00000004,
|
||||
VTF_FLAGS_CLAMP_T = 0x00000008,
|
||||
VTF_FLAGS_ANISOTROPIC = 0x00000010,
|
||||
VTF_FLAGS_HINT_DXT5 = 0x00000020,
|
||||
VTF_FLAGS_NOCOMPRESS = 0x00000040,
|
||||
VTF_FLAGS_NORMAL = 0x00000080,
|
||||
VTF_FLAGS_NOMIP = 0x00000100,
|
||||
VTF_FLAGS_NOLOD = 0x00000200,
|
||||
VTF_FLAGS_MINMIP = 0x00000400,
|
||||
VTF_FLAGS_PROCEDURAL = 0x00000800,
|
||||
VTF_FLAGS_ONEBITALPHA = 0x00001000,
|
||||
VTF_FLAGS_EIGHTBITALPHA = 0x00002000,
|
||||
VTF_FLAGS_ENVMAP = 0x00004000,
|
||||
VTF_FLAGS_RENDERTARGET = 0x00008000,
|
||||
VTF_FLAGS_DEPTHRENDERTARGET = 0x00010000,
|
||||
VTF_FLAGS_NODEBUGOVERRIDE = 0x00020000,
|
||||
VTF_FLAGS_SINGLECOPY = 0x00040000,
|
||||
VTF_FLAGS_ONEOVERMIPLEVELINALPHA = 0x00080000,
|
||||
VTF_FLAGS_PREMULTCOLORBYONEOVERMIPLEVEL = 0x00100000,
|
||||
VTF_FLAGS_NORMALTODUDV = 0x00200000,
|
||||
VTF_FLAGS_ALPHATESTMIPGENERATION = 0x00400000,
|
||||
VTF_FLAGS_NODEPTHBUFFER = 0x00800000,
|
||||
VTF_FLAGS_NICEFILTERED = 0x01000000,
|
||||
VTF_FLAGS_CLAMP_U = 0x02000000,
|
||||
VTF_FLAGS_PRESWIZZLED = 0x04000000,
|
||||
VTF_FLAGS_CACHEABLE = 0x08000000,
|
||||
VTF_FLAGS_UNFILTERABLE_OK = 0x10000000,
|
||||
VTF_FLAGS_LASTFLAG = 0x10000000
|
||||
};
|
||||
|
||||
|
||||
enum VTFCubeMapFaceIndex
|
||||
{
|
||||
VTF_CUBEMAP_FACE_RIGHT = 0,
|
||||
VTF_CUBEMAP_FACE_LEFT,
|
||||
VTF_CUBEMAP_FACE_BACK,
|
||||
VTF_CUBEMAP_FACE_FRONT,
|
||||
VTF_CUBEMAP_FACE_UP,
|
||||
VTF_CUBEMAP_FACE_DOWN,
|
||||
VTF_CUBEMAP_FACE_SPHEREMAP,
|
||||
VTF_CUBEMAP_FACE_COUNT
|
||||
};
|
||||
|
||||
|
||||
enum VTFLookDir
|
||||
{
|
||||
VTF_LOOK_DOWN_X = 0,
|
||||
VTF_LOOK_DOWN_NEGX,
|
||||
VTF_LOOK_DOWN_Y = 0,
|
||||
VTF_LOOK_DOWN_NEGY,
|
||||
VTF_LOOK_DOWN_Z = 0,
|
||||
VTF_LOOK_DOWN_NEGZ
|
||||
};
|
||||
|
||||
|
||||
enum VTFImageFormat
|
||||
{
|
||||
VTF_FORMAT_UNKNOWN = -1,
|
||||
VTF_FORMAT_RGBA8888 = 0,
|
||||
VTF_FORMAT_ABGR8888,
|
||||
VTF_FORMAT_RGB888,
|
||||
VTF_FORMAT_BGR888,
|
||||
VTF_FORMAT_RGB565,
|
||||
VTF_FORMAT_I8,
|
||||
VTF_FORMAT_IA88,
|
||||
VTF_FORMAT_P8,
|
||||
VTF_FORMAT_A8,
|
||||
VTF_FORMAT_RGB888_BLUESCREEN,
|
||||
VTF_FORMAT_BGR888_BLUESCREEN,
|
||||
VTF_FORMAT_ARGB8888,
|
||||
VTF_FORMAT_BGRA8888,
|
||||
VTF_FORMAT_DXT1,
|
||||
VTF_FORMAT_DXT3,
|
||||
VTF_FORMAT_DXT5,
|
||||
VTF_FORMAT_BGRX8888,
|
||||
VTF_FORMAT_BGR565,
|
||||
VTF_FORMAT_BGRX5551,
|
||||
VTF_FORMAT_BGRA4444,
|
||||
VTF_FORMAT_DXT1_ONEBITALPHA,
|
||||
VTF_FORMAT_BGRA5551,
|
||||
VTF_FORMAT_UV88,
|
||||
VTF_FORMAT_UVWQ8888,
|
||||
VTF_FORMAT_RGBA16161616F,
|
||||
VTF_FORMAT_RGBA16161616,
|
||||
VTF_FORMAT_UVLX8888,
|
||||
VTF_FORMAT_R32F,
|
||||
VTF_FORMAT_RGB323232F,
|
||||
VTF_FORMAT_RGBA32323232F,
|
||||
VTF_NUM_IMAGE_FORMATS
|
||||
};
|
||||
|
||||
|
||||
#define VTF_FORMAT_DEFAULT ((VTFImageFormat)-2)
|
||||
|
||||
|
||||
struct VTFFileHeader
|
||||
{
|
||||
char magic_number[4];
|
||||
unsigned int file_version[2];
|
||||
unsigned int header_size;
|
||||
unsigned short image_width;
|
||||
unsigned short image_height;
|
||||
unsigned int image_flags;
|
||||
unsigned short num_frames;
|
||||
unsigned short start_frame;
|
||||
|
||||
unsigned char padding_0[4];
|
||||
osg::Vec3f reflectivity_value;
|
||||
unsigned char padding_1[4];
|
||||
|
||||
float bump_scale;
|
||||
unsigned int image_format;
|
||||
unsigned char num_mip_levels;
|
||||
unsigned char low_res_image_format;
|
||||
unsigned char padding_2[3];
|
||||
unsigned char low_res_image_width;
|
||||
unsigned char low_res_image_height;
|
||||
unsigned short image_depth;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Structure of a DXT-1 compressed texture block
|
||||
// see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/directx9_c/Opaque_and_1_Bit_Alpha_Textures.asp
|
||||
//
|
||||
struct DXT1TexelsBlock
|
||||
{
|
||||
unsigned short color_0; // colors at their
|
||||
unsigned short color_1; // extreme
|
||||
unsigned int texels4x4; // interpolated colors (2 bits per texel)
|
||||
};
|
||||
|
||||
|
||||
bool ConvertImageFormat(unsigned int vtfFormat, int& internalFormat,
|
||||
int& pixelFormat, int& dataType)
|
||||
{
|
||||
bool supported;
|
||||
|
||||
// Assume a supported format to start
|
||||
supported = true;
|
||||
|
||||
// Decode the format
|
||||
switch (vtfFormat)
|
||||
{
|
||||
case VTF_FORMAT_DEFAULT:
|
||||
supported = false;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_UNKNOWN:
|
||||
supported = false;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_RGBA8888:
|
||||
internalFormat = GL_RGBA;
|
||||
pixelFormat = GL_RGBA;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_ABGR8888:
|
||||
internalFormat = GL_RGBA;
|
||||
pixelFormat = GL_ABGR_EXT;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_RGB888:
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_RGB;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGR888:
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_BGR;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_RGB565:
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_RGB;
|
||||
dataType = GL_UNSIGNED_SHORT_5_6_5;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_I8:
|
||||
internalFormat = GL_LUMINANCE;
|
||||
pixelFormat = GL_LUMINANCE;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_IA88:
|
||||
internalFormat = GL_LUMINANCE_ALPHA;
|
||||
pixelFormat = GL_LUMINANCE_ALPHA;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_P8:
|
||||
// 8-bit paletted image, not supported
|
||||
supported = false;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_A8:
|
||||
internalFormat = GL_ALPHA;
|
||||
pixelFormat = GL_ALPHA;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_RGB888_BLUESCREEN:
|
||||
// Ignore the "bluescreen" specification for now
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_RGB;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGR888_BLUESCREEN:
|
||||
// Ignore the "bluescreen" specification for now
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_BGR;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_ARGB8888:
|
||||
// ARGB not supported
|
||||
supported = false;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGRA8888:
|
||||
internalFormat = GL_RGBA;
|
||||
pixelFormat = GL_BGRA;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_DXT1:
|
||||
internalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
pixelFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_DXT3:
|
||||
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_DXT5:
|
||||
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGRX8888:
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_BGRA;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGR565:
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_BGR;
|
||||
dataType = GL_UNSIGNED_SHORT_5_6_5_REV;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGRX5551:
|
||||
internalFormat = GL_RGB;
|
||||
pixelFormat = GL_BGRA;
|
||||
dataType = GL_UNSIGNED_SHORT_5_5_5_1;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGRA4444:
|
||||
internalFormat = GL_RGBA;
|
||||
pixelFormat = GL_BGRA;
|
||||
dataType = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_DXT1_ONEBITALPHA:
|
||||
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
dataType = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_BGRA5551:
|
||||
internalFormat = GL_RGBA;
|
||||
pixelFormat = GL_BGRA;
|
||||
dataType = GL_UNSIGNED_SHORT_5_5_5_1;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_UV88:
|
||||
supported = false;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_UVWQ8888:
|
||||
supported = false;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_RGBA16161616F:
|
||||
internalFormat = GL_RGBA;
|
||||
pixelFormat = GL_RGBA;
|
||||
dataType = GL_HALF_FLOAT_NV;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_RGBA16161616:
|
||||
internalFormat = GL_RGBA;
|
||||
pixelFormat = GL_RGBA;
|
||||
dataType = GL_UNSIGNED_SHORT;
|
||||
break;
|
||||
|
||||
case VTF_FORMAT_UVLX8888:
|
||||
supported = false;
|
||||
break;
|
||||
|
||||
default:
|
||||
supported = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Return whether or not the format is supported
|
||||
return supported;
|
||||
}
|
||||
|
||||
|
||||
osg::Image* ReadVTFFile(std::istream& _istream)
|
||||
{
|
||||
VTFFileHeader vtf_header;
|
||||
bool supported;
|
||||
int internalFormat;
|
||||
int pixelFormat;
|
||||
int dataType;
|
||||
int s, t, r;
|
||||
unsigned int lrSize;
|
||||
unsigned char * imageData;
|
||||
unsigned char * imageDataPtr;
|
||||
unsigned int base;
|
||||
unsigned int size;
|
||||
int mip;
|
||||
int mipSize;
|
||||
int mipOffset;
|
||||
|
||||
// Validate the file with the 'VTF\0' magic number
|
||||
_istream.read(&vtf_header.magic_number[0], 4);
|
||||
if ((vtf_header.magic_number[0] != 'V') ||
|
||||
(vtf_header.magic_number[1] != 'T') ||
|
||||
(vtf_header.magic_number[2] != 'F') ||
|
||||
(vtf_header.magic_number[3] != 0))
|
||||
{
|
||||
// Not a VTF file, so bail
|
||||
osg::notify(osg::WARN) << "VTF file is invalid" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Read next two fields of the header (which includes the header size)
|
||||
_istream.read((char *)&vtf_header.file_version[0], 12);
|
||||
|
||||
// Now, read the rest of the header
|
||||
_istream.read((char *)&vtf_header.image_width, vtf_header.header_size - 16);
|
||||
|
||||
// No depth in textures earlier than version 7.2
|
||||
if ((vtf_header.file_version[0] < 7) ||
|
||||
((vtf_header.file_version[0] == 7) &&
|
||||
(vtf_header.file_version[1] < 2)))
|
||||
vtf_header.image_depth = 1;
|
||||
|
||||
// Environment maps not supported
|
||||
if (vtf_header.image_flags & VTF_FLAGS_ENVMAP)
|
||||
{
|
||||
osg::notify(osg::WARN) << "VTF Environment maps not supported";
|
||||
osg::notify(osg::WARN) << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
osg::notify(osg::INFO) << "VTF Header: (" << sizeof(VTFFileHeader);
|
||||
osg::notify(osg::INFO) << " bytes)" << std::endl;
|
||||
osg::notify(osg::INFO) << " magic_number = ";
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[0];
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[1];
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[2];
|
||||
osg::notify(osg::INFO) << vtf_header.magic_number[3] << std:: endl;
|
||||
osg::notify(osg::INFO) << " file_version = ";
|
||||
osg::notify(osg::INFO) << vtf_header.file_version[0] << ".";
|
||||
osg::notify(osg::INFO) << vtf_header.file_version[1] << std:: endl;
|
||||
osg::notify(osg::INFO) << " header_size = ";
|
||||
osg::notify(osg::INFO) << vtf_header.header_size << std::endl;
|
||||
osg::notify(osg::INFO) << " image_width = ";
|
||||
osg::notify(osg::INFO) << vtf_header.image_width << std::endl;
|
||||
osg::notify(osg::INFO) << " image_height = ";
|
||||
osg::notify(osg::INFO) << vtf_header.image_height << std::endl;
|
||||
osg::notify(osg::INFO) << " num_frames = ";
|
||||
osg::notify(osg::INFO) << vtf_header.num_frames << std::endl;
|
||||
osg::notify(osg::INFO) << " start_frame = ";
|
||||
osg::notify(osg::INFO) << vtf_header.start_frame << std::endl;
|
||||
osg::notify(osg::INFO) << " reflectivity = ";
|
||||
osg::notify(osg::INFO) << vtf_header.reflectivity_value.x() << ", ";
|
||||
osg::notify(osg::INFO) << vtf_header.reflectivity_value.y() << ", ";
|
||||
osg::notify(osg::INFO) << vtf_header.reflectivity_value.z() << std::endl;
|
||||
osg::notify(osg::INFO) << " bump_scale = ";
|
||||
osg::notify(osg::INFO) << vtf_header.bump_scale << std::endl;
|
||||
osg::notify(osg::INFO) << " image_format = ";
|
||||
osg::notify(osg::INFO) << vtf_header.image_format << std::endl;
|
||||
osg::notify(osg::INFO) << " num_mip_lvls = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.num_mip_levels << std::endl;
|
||||
osg::notify(osg::INFO) << " lr_image_fmt = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.low_res_image_format << std::endl;
|
||||
osg::notify(osg::INFO) << " lr_width = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.low_res_image_width << std::endl;
|
||||
osg::notify(osg::INFO) << " lr_height = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.low_res_image_height << std::endl;
|
||||
osg::notify(osg::INFO) << " image_depth = ";
|
||||
osg::notify(osg::INFO) << (int)vtf_header.image_depth << std::endl;
|
||||
|
||||
// Before we get to the real image, we need to skip over the "low res"
|
||||
// image that's often stored along with VTF textures, so get the
|
||||
// low-res image dimensions
|
||||
s = vtf_header.low_res_image_width;
|
||||
t = vtf_header.low_res_image_height;
|
||||
r = 1;
|
||||
osg::notify(osg::INFO) << "Low-res s = " << s << std::endl;
|
||||
osg::notify(osg::INFO) << "Low-res t = " << t << std::endl;
|
||||
|
||||
// See if the low-res image is there
|
||||
lrSize = 0;
|
||||
if ((s > 0) && (t > 0))
|
||||
{
|
||||
supported = ConvertImageFormat(vtf_header.low_res_image_format,
|
||||
internalFormat, pixelFormat, dataType);
|
||||
|
||||
// If we don't recognize the format, we can't locate the real image
|
||||
// in the file, so we have to bail
|
||||
if (!supported)
|
||||
{
|
||||
osg::notify(osg::WARN) << "Low-res image format is not supported";
|
||||
osg::notify(osg::WARN) << " (" << vtf_header.low_res_image_format;
|
||||
osg::notify(osg::WARN) << ")" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Allocate an osg::Image for the lo-res image metadata
|
||||
osg::ref_ptr<osg::Image> loResImage = new osg::Image();
|
||||
|
||||
// Set the image metadata, and figure out how many bytes to read
|
||||
loResImage->setImage(s, t, r, internalFormat, pixelFormat, dataType,
|
||||
0, osg::Image::USE_NEW_DELETE);
|
||||
lrSize = loResImage->getTotalSizeInBytes();
|
||||
|
||||
// Skip over the low-res image data
|
||||
osg::notify(osg::INFO) << "Low-res size = " << lrSize << std::endl;
|
||||
_istream.ignore(lrSize);
|
||||
}
|
||||
|
||||
// Compute the base position of the high-res image data
|
||||
base = vtf_header.header_size + lrSize;
|
||||
|
||||
// Now, get the internal format, pixel format, and data type from the
|
||||
// full-size image format, and check whether the format is supported
|
||||
supported = ConvertImageFormat(vtf_header.image_format, internalFormat,
|
||||
pixelFormat, dataType);
|
||||
|
||||
// Bail if the format isn't supported
|
||||
if (!supported)
|
||||
{
|
||||
osg::notify(osg::WARN) << "Image format is not supported (";
|
||||
osg::notify(osg::WARN) << vtf_header.image_format << ")";
|
||||
osg::notify(osg::WARN) << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the dimensions of the image
|
||||
s = vtf_header.image_width;
|
||||
t = vtf_header.image_height;
|
||||
r = vtf_header.image_depth;
|
||||
|
||||
// VTF allows either 0 or 1 for 2D images
|
||||
if (r == 0)
|
||||
r = 1;
|
||||
|
||||
// NOTE: VTF supports animated textures and cube maps. Currently, we
|
||||
// only handle a single frame of data, so multiple frames
|
||||
// are ignored. Same for cube maps (only one face is loaded).
|
||||
|
||||
// Create the mipmap offsets vector
|
||||
osg::Image::MipmapDataType mipmaps;
|
||||
|
||||
// Deal with mipmaps, if necessary
|
||||
if (vtf_header.num_mip_levels > 1)
|
||||
{
|
||||
// Set up the offsets vector
|
||||
float power2_s = logf((float)s)/logf((float)2);
|
||||
float power2_t = logf((float)t)/logf((float)2);
|
||||
mipmaps.resize((unsigned int)osg::maximum(power2_s,power2_t),0);
|
||||
|
||||
// Calculate the dimensions of each mipmap
|
||||
if ((vtf_header.image_format == VTF_FORMAT_DXT1) ||
|
||||
(vtf_header.image_format == VTF_FORMAT_DXT1_ONEBITALPHA) ||
|
||||
(vtf_header.image_format == VTF_FORMAT_DXT3) ||
|
||||
(vtf_header.image_format == VTF_FORMAT_DXT5))
|
||||
{
|
||||
// Handle S3TC compressed mipmaps
|
||||
int width = vtf_header.image_width;
|
||||
int height = vtf_header.image_height;
|
||||
int blockSize;
|
||||
|
||||
if ((vtf_header.image_format == VTF_FORMAT_DXT1) ||
|
||||
(vtf_header.image_format == VTF_FORMAT_DXT1_ONEBITALPHA))
|
||||
blockSize = 8;
|
||||
else
|
||||
blockSize = 16;
|
||||
|
||||
int offset = 0;
|
||||
for (unsigned int k = 1;
|
||||
(k < vtf_header.num_mip_levels) && (width || height);
|
||||
++k)
|
||||
{
|
||||
// Clamp dimensions to 1
|
||||
if (width == 0)
|
||||
width = 1;
|
||||
if (height == 0)
|
||||
height = 1;
|
||||
|
||||
// Compute and store the offset into the final image data
|
||||
offset += (((width+3)/4) * ((height+3)/4) * blockSize);
|
||||
mipmaps[k-1] = offset;
|
||||
|
||||
// Get the next level's dimensions
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Handle uncompressed mipmaps
|
||||
int offset = 0;
|
||||
int width = vtf_header.image_width;
|
||||
int height = vtf_header.image_height;
|
||||
int depth = vtf_header.image_depth;
|
||||
for (unsigned int k = 1;
|
||||
(k < vtf_header.num_mip_levels) && (width || height || depth);
|
||||
++k)
|
||||
{
|
||||
if (width == 0)
|
||||
width = 1;
|
||||
if (height == 0)
|
||||
height = 1;
|
||||
if (depth == 0)
|
||||
depth = 1;
|
||||
|
||||
// Compute and store the offset into the final image data
|
||||
offset += depth * height *
|
||||
osg::Image::computeRowWidthInBytes(width, pixelFormat,
|
||||
dataType, 1 );
|
||||
mipmaps[k-1] = offset;
|
||||
|
||||
// Get the next level's dimensions
|
||||
width >>= 1;
|
||||
height >>= 1;
|
||||
depth >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate the resulting osg::Image
|
||||
osg::ref_ptr<osg::Image> osgImage = new osg::Image();
|
||||
|
||||
// Set the image meta-data, including dimensions, format, data type,
|
||||
// and mipmap levels. Everything but the image data itself. We'll use
|
||||
// this to compute the total image size, so we know how much data to read
|
||||
// from the file
|
||||
osgImage->setImage(s, t, r, internalFormat, pixelFormat, dataType,
|
||||
0, osg::Image::USE_NEW_DELETE);
|
||||
if (mipmaps.size() > 0)
|
||||
osgImage->setMipmapLevels(mipmaps);
|
||||
|
||||
// Compute the total image size
|
||||
size = osgImage->getTotalSizeInBytesIncludingMipmaps();
|
||||
osg::notify(osg::INFO) << "ReadVTFFile info : size = " << size << std::endl;
|
||||
if(size <= 0)
|
||||
{
|
||||
osg::notify(osg::WARN) << "ReadVTFFile warning: size <= 0" << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Prepare to read the image data
|
||||
imageData = new unsigned char [size];
|
||||
if(!imageData)
|
||||
{
|
||||
osg::notify(osg::WARN) << "ReadVTFFile warning: imageData == NULL";
|
||||
osg::notify(osg::WARN) << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// See if we have mipmaps
|
||||
if (vtf_header.num_mip_levels > 1)
|
||||
{
|
||||
// VTF stores the mipmaps in reverse order from what OpenGL expects, so
|
||||
// we need to read them from the file and store them in order in the
|
||||
// image data array
|
||||
for (mip = vtf_header.num_mip_levels - 2; mip >= 0; mip--)
|
||||
{
|
||||
// Look up the offset for this mip level
|
||||
mipOffset = mipmaps[mip];
|
||||
|
||||
// Calculate the size of the mipmap
|
||||
if (mip == vtf_header.num_mip_levels-2)
|
||||
mipSize = size - mipOffset;
|
||||
else
|
||||
mipSize = mipmaps[mip+1] - mipOffset;
|
||||
|
||||
// Read the image data
|
||||
_istream.read((char*)&imageData[mipOffset], mipSize);
|
||||
}
|
||||
|
||||
// We've read all of the mipmaps except the largest (the original,
|
||||
// image), so do that now
|
||||
mipSize = mipmaps[1];
|
||||
_istream.read((char*)imageData, mipSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just read the image data
|
||||
_istream.read((char*)imageData, size);
|
||||
}
|
||||
|
||||
/*
|
||||
// Check if alpha information embedded in the 8-byte encoding blocks
|
||||
if (checkIfUsingOneBitAlpha)
|
||||
{
|
||||
const DXT1TexelsBlock *texelsBlock =
|
||||
reinterpret_cast<const DXT1TexelsBlock*>(imageData);
|
||||
|
||||
// Only do the check on the first mipmap level
|
||||
unsigned int numBlocks = mipmaps.size()>0 ? mipmaps[0] / 8 : size / 8;
|
||||
|
||||
for (int i=numBlocks; i>0; --i, ++texelsBlock)
|
||||
{
|
||||
if (texelsBlock->color_0<=texelsBlock->color_1)
|
||||
{
|
||||
// Texture is using the 1-bit alpha encoding, so we need to
|
||||
// update the assumed pixel format
|
||||
internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
pixelFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Now, set the actual image data and mipmap levels
|
||||
osgImage->setImage(s,t,r, internalFormat, pixelFormat, dataType,
|
||||
imageData, osg::Image::USE_NEW_DELETE);
|
||||
if (mipmaps.size()>0) osgImage->setMipmapLevels(mipmaps);
|
||||
|
||||
// Finally, return the image
|
||||
return osgImage.release();
|
||||
}
|
||||
|
||||
|
||||
bool WriteVTFFile(const osg::Image *img, std::ostream& fout)
|
||||
{
|
||||
// Not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
class ReaderWriterVTF : public osgDB::ReaderWriter
|
||||
{
|
||||
public:
|
||||
virtual const char* className() const
|
||||
{
|
||||
return "VTF Image Reader/Writer";
|
||||
}
|
||||
|
||||
virtual bool acceptsExtension(const std::string& extension) const
|
||||
{
|
||||
return osgDB::equalCaseInsensitive(extension, "vtf");
|
||||
}
|
||||
|
||||
virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
return readImage(file,options);
|
||||
}
|
||||
|
||||
virtual ReadResult readObject(std::istream& fin, const Options* options) const
|
||||
{
|
||||
return readImage(fin,options);
|
||||
}
|
||||
|
||||
virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
std::string ext = osgDB::getLowerCaseFileExtension(file);
|
||||
if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
std::string fileName = osgDB::findDataFile( file, options );
|
||||
|
||||
if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
|
||||
|
||||
std::ifstream stream(fileName.c_str(), std::ios::in | std::ios::binary);
|
||||
if(!stream) return ReadResult::FILE_NOT_HANDLED;
|
||||
ReadResult rr = readImage(stream, options);
|
||||
if(rr.validImage()) rr.getImage()->setFileName(file);
|
||||
return rr;
|
||||
}
|
||||
|
||||
virtual ReadResult readImage(std::istream& fin, const Options* options) const
|
||||
{
|
||||
osg::Image* osgImage = ReadVTFFile(fin);
|
||||
if (osgImage==NULL) return ReadResult::FILE_NOT_HANDLED;
|
||||
|
||||
if (options && options->getOptionString().find("vtf_flip")!=std::string::npos)
|
||||
{
|
||||
osgImage->flipVertical();
|
||||
}
|
||||
|
||||
return osgImage;
|
||||
}
|
||||
|
||||
virtual WriteResult writeObject(const osg::Object& object,const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
const osg::Image* image = dynamic_cast<const osg::Image*>(&object);
|
||||
if (!image) return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
return writeImage(*image,file,options);
|
||||
}
|
||||
|
||||
virtual WriteResult writeObject(const osg::Object& object,std::ostream& fout,const Options* options) const
|
||||
{
|
||||
const osg::Image* image = dynamic_cast<const osg::Image*>(&object);
|
||||
if (!image) return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
return writeImage(*image,fout,options);
|
||||
}
|
||||
|
||||
|
||||
virtual WriteResult writeImage(const osg::Image &image,const std::string& file, const osgDB::ReaderWriter::Options* options) const
|
||||
{
|
||||
std::string ext = osgDB::getFileExtension(file);
|
||||
if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
|
||||
|
||||
std::ofstream fout(file.c_str(), std::ios::out | std::ios::binary);
|
||||
if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
|
||||
|
||||
return writeImage(image,fout,options);
|
||||
}
|
||||
|
||||
virtual WriteResult writeImage(const osg::Image& image,std::ostream& fout,const Options*) const
|
||||
{
|
||||
bool success = WriteVTFFile(&image, fout);
|
||||
|
||||
if(success)
|
||||
return WriteResult::FILE_SAVED;
|
||||
else
|
||||
return WriteResult::ERROR_IN_WRITING_FILE;
|
||||
}
|
||||
};
|
||||
|
||||
// now register with Registry to instantiate the above
|
||||
// reader/writer.
|
||||
REGISTER_OSGPLUGIN(vtf, ReaderWriterVTF)
|
||||
Reference in New Issue
Block a user