compressed textures, texture objects

This commit is contained in:
David Rose 2006-03-15 20:05:36 +00:00
parent 4f9c62a0d6
commit e6369ba5a4
42 changed files with 2348 additions and 277 deletions

View File

@ -744,6 +744,63 @@ flip_frame() {
do_flip_frame();
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::extract_texture_data
// Access: Published
// Description: Asks the indicated GraphicsStateGuardian to retrieve
// the texture memory image of the indicated texture and
// store it in the texture's ram_image field. The image
// can then be written to disk via Texture::write(), or
// otherwise manipulated on the CPU.
//
// This is useful for retrieving the contents of a
// texture that has been somehow generated on the
// graphics card, instead of having been loaded the
// normal way via Texture::read() or Texture::load().
// It is particularly useful for getting the data
// associated with a compressed texture image.
//
// Since this requires a round-trip to the draw thread,
// it may require waiting for the current thread to
// finish rendering if it is called in a multithreaded
// environment. However, you can call this several
// consecutive times on different textures for little
// additional cost.
//
// If the texture has not yet been loaded to the GSG in
// question, it will be loaded immediately.
//
// The return value is true if the operation is
// successful, false otherwise.
////////////////////////////////////////////////////////////////////
bool GraphicsEngine::
extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
MutexHolder holder(_lock);
string draw_name = gsg->get_threading_model().get_draw_name();
if (draw_name.empty()) {
// A single-threaded environment. No problem.
return gsg->extract_texture_data(tex);
} else {
// A multi-threaded environment. We have to wait until the draw
// thread has finished its current task.
WindowRenderer *wr = get_window_renderer(draw_name, 0);
RenderThread *thread = (RenderThread *)wr;
MutexHolder holder2(thread->_cv_mutex);
while (thread->_thread_state != TS_wait) {
thread->_cv_done.wait();
}
// OK, now the draw thread is idle. That's really good enough for
// our purposes; we don't *actually* need to make the draw thread
// do the work--it's sufficient that it's not doing anything else
// while we access the GSG.
return gsg->extract_texture_data(tex);
}
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsEngine::add_callback

View File

@ -97,6 +97,8 @@ PUBLISHED:
void sync_frame();
void flip_frame();
bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg);
public:
enum ThreadState {
TS_wait,

View File

@ -356,6 +356,29 @@ get_supports_tex_non_pow2() const {
return _supports_tex_non_pow2;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_supports_compressed_texture
// Access: Published
// Description: Returns true if this GSG can compress textures as it
// loads them into texture memory, and/or accept
// pre-compressed textures for storing.
////////////////////////////////////////////////////////////////////
INLINE bool GraphicsStateGuardian::
get_supports_compressed_texture() const {
return _supports_compressed_texture;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_supports_compressed_texture_format
// Access: Published
// Description: Returns true if this GSG can accept textures
// pre-compressed in the indicated format.
////////////////////////////////////////////////////////////////////
INLINE bool GraphicsStateGuardian::
get_supports_compressed_texture_format(Texture::CompressionMode compression) const {
return _compressed_texture_formats.get_bit(compression);
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::get_max_lights
// Access: Published

View File

@ -136,6 +136,9 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
_supports_3d_texture = false;
_supports_cube_map = false;
_supports_tex_non_pow2 = false;
_supports_compressed_texture = false;
_compressed_texture_formats.clear();
_compressed_texture_formats.set_bit(Texture::CM_off);
// Assume no limits on number of lights or clip planes.
_max_lights = -1;
@ -415,6 +418,23 @@ void GraphicsStateGuardian::
release_texture(TextureContext *) {
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::extract_texture_data
// Access: Public, Virtual
// Description: This method should only be called by the
// GraphicsEngine. Do not call it directly; call
// GraphicsEngine::extract_texture_data() instead.
//
// This method will be called in the draw thread to
// download the texture memory's image into its
// ram_image value. It returns true on success, false
// otherwise.
////////////////////////////////////////////////////////////////////
bool GraphicsStateGuardian::
extract_texture_data(Texture *) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: GraphicsStateGuardian::prepare_geom
// Access: Public, Virtual

View File

@ -43,6 +43,8 @@
#include "pvector.h"
#include "attribSlots.h"
#include "shaderContext.h"
#include "bitMask.h"
#include "texture.h"
class DrawableRegion;
class GraphicsEngine;
@ -99,6 +101,9 @@ PUBLISHED:
INLINE bool get_supports_cube_map() const;
INLINE bool get_supports_tex_non_pow2() const;
INLINE bool get_supports_compressed_texture() const;
INLINE bool get_supports_compressed_texture_format(Texture::CompressionMode compression) const;
INLINE int get_max_lights() const;
INLINE int get_max_clip_planes() const;
@ -132,6 +137,7 @@ public:
virtual TextureContext *prepare_texture(Texture *tex);
virtual void release_texture(TextureContext *tc);
virtual bool extract_texture_data(Texture *tex);
virtual GeomContext *prepare_geom(Geom *geom);
virtual void release_geom(GeomContext *gc);
@ -347,6 +353,9 @@ protected:
bool _supports_cube_map;
bool _supports_tex_non_pow2;
bool _supports_compressed_texture;
BitMask32 _compressed_texture_formats;
int _max_lights;
int _max_clip_planes;

View File

@ -148,6 +148,13 @@ DXGraphicsStateGuardian8::
TextureContext *DXGraphicsStateGuardian8::
prepare_texture(Texture *tex) {
DXTextureContext8 *dtc = new DXTextureContext8(tex);
if (!get_supports_compressed_texture_format(tex->get_ram_image_compression())) {
dxgsg8_cat.error()
<< *dtc->_texture << " is stored in an unsupported compressed format.\n";
return NULL;
}
if (!dtc->create_texture(*_screen)) {
delete dtc;
return NULL;
@ -189,7 +196,14 @@ apply_texture(int i, TextureContext *tc) {
// error or oversight.
if ((dirty & Texture::DF_image) == 0) {
dxgsg8_cat.warning()
<< "Texture " << *dtc->_texture << " has changed mipmap state.\n";
<< *dtc->_texture << " has changed mipmap state.\n";
}
if (!get_supports_compressed_texture_format(tc->_texture->get_ram_image_compression())) {
dxgsg8_cat.error()
<< *dtc->_texture << " is stored in an unsupported compressed format.\n";
_d3d_device->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
return;
}
if (!dtc->create_texture(*_screen)) {

View File

@ -191,6 +191,13 @@ DXGraphicsStateGuardian9::
TextureContext *DXGraphicsStateGuardian9::
prepare_texture(Texture *tex) {
DXTextureContext9 *dtc = new DXTextureContext9(tex);
if (!get_supports_compressed_texture_format(tex->get_ram_image_compression())) {
dxgsg9_cat.error()
<< *dtc->_texture << " is stored in an unsupported compressed format.\n";
return NULL;
}
if (!dtc->create_texture(*_screen)) {
delete dtc;
return NULL;
@ -237,7 +244,14 @@ apply_texture(int i, TextureContext *tc) {
// error or oversight.
if ((dirty & Texture::DF_image) == 0) {
dxgsg9_cat.warning()
<< "Texture " << *dtc->_texture << " has changed mipmap state.\n";
<< *dtc->_texture << " has changed mipmap state.\n";
}
if (!get_supports_compressed_texture_format(tc->_texture->get_ram_image_compression())) {
dxgsg9_cat.error()
<< *dtc->_texture << " is stored in an unsupported compressed format.\n";
_d3d_device->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
return;
}
if (!dtc->create_texture(*_screen)) {

View File

@ -63,6 +63,26 @@ get_format() const {
return _format;
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::set_compression_mode
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void EggTexture::
set_compression_mode(CompressionMode mode) {
_compression_mode = mode;
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::get_compression_mode
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE EggTexture::CompressionMode EggTexture::
get_compression_mode() const {
return _compression_mode;
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::set_wrap_mode
// Access: Published

View File

@ -37,6 +37,7 @@ EggTexture(const string &tref_name, const string &filename)
{
_texture_type = TT_unspecified;
_format = F_unspecified;
_compression_mode = CM_default;
_wrap_mode = WM_unspecified;
_wrap_u = WM_unspecified;
_wrap_v = WM_unspecified;
@ -80,6 +81,7 @@ operator = (const EggTexture &copy) {
_texture_type = copy._texture_type;
_format = copy._format;
_compression_mode = copy._compression_mode;
_wrap_mode = copy._wrap_mode;
_wrap_u = copy._wrap_u;
_wrap_v = copy._wrap_v;
@ -152,6 +154,11 @@ write(ostream &out, int indent_level) const {
<< "<Scalar> format { " << get_format() << " }\n";
}
if (get_compression_mode() != CM_default) {
indent(out, indent_level + 2)
<< "<Scalar> compression { " << get_compression_mode() << " }\n";
}
if (get_wrap_mode() != WM_unspecified) {
indent(out, indent_level + 2)
<< "<Scalar> wrap { " << get_wrap_mode() << " }\n";
@ -360,6 +367,7 @@ is_equivalent_to(const EggTexture &other, int eq) const {
//cout << "compared by attributes" << endl;
if (_texture_type != other._texture_type ||
_format != other._format ||
_compression_mode != other._compression_mode ||
_wrap_mode != other._wrap_mode ||
_wrap_u != other._wrap_u ||
_wrap_v != other._wrap_v ||
@ -442,6 +450,9 @@ sorts_less_than(const EggTexture &other, int eq) const {
if (_format != other._format) {
return (int)_format < (int)other._format;
}
if (_compression_mode != other._compression_mode) {
return (int)_compression_mode < (int)other._compression_mode;
}
if (_wrap_mode != other._wrap_mode) {
return (int)_wrap_mode < (int)other._wrap_mode;
}
@ -617,7 +628,7 @@ string_texture_type(const string &string) {
} else if (cmp_nocase_uh(string, "3d") == 0 ||
cmp_nocase_uh(string, "3dtexture") == 0 ||
cmp_nocase_uh(string, "3d_texture") == 0) {
return TT_2d_texture;
return TT_3d_texture;
} else if (cmp_nocase_uh(string, "cube") == 0 ||
cmp_nocase_uh(string, "cubemap") == 0 ||
@ -680,6 +691,36 @@ string_format(const string &string) {
}
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::string_compression_mode
// Access: Published, Static
// Description: Returns the CompressionMode value associated with the given
// string representation, or CM_default if the string
// does not match any known CompressionMode value.
////////////////////////////////////////////////////////////////////
EggTexture::CompressionMode EggTexture::
string_compression_mode(const string &string) {
if (cmp_nocase_uh(string, "off") == 0) {
return CM_off;
} else if (cmp_nocase_uh(string, "on") == 0) {
return CM_on;
} else if (cmp_nocase_uh(string, "fxt1") == 0) {
return CM_fxt1;
} else if (cmp_nocase_uh(string, "dxt1") == 0) {
return CM_dxt1;
} else if (cmp_nocase_uh(string, "dxt2") == 0) {
return CM_dxt2;
} else if (cmp_nocase_uh(string, "dxt3") == 0) {
return CM_dxt3;
} else if (cmp_nocase_uh(string, "dxt4") == 0) {
return CM_dxt4;
} else if (cmp_nocase_uh(string, "dxt5") == 0) {
return CM_dxt5;
} else {
return CM_default;
}
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::string_wrap_mode
// Access: Published, Static
@ -1067,6 +1108,36 @@ ostream &operator << (ostream &out, EggTexture::Format format) {
return out << "(**invalid**)";
}
////////////////////////////////////////////////////////////////////
// Function: CompressionMode output operator
// Description:
////////////////////////////////////////////////////////////////////
ostream &operator << (ostream &out, EggTexture::CompressionMode mode) {
switch (mode) {
case EggTexture::CM_default:
return out << "default";
case EggTexture::CM_off:
return out << "off";
case EggTexture::CM_on:
return out << "on";
case EggTexture::CM_fxt1:
return out << "fxt1";
case EggTexture::CM_dxt1:
return out << "dxt1";
case EggTexture::CM_dxt2:
return out << "dxt2";
case EggTexture::CM_dxt3:
return out << "dxt3";
case EggTexture::CM_dxt4:
return out << "dxt4";
case EggTexture::CM_dxt5:
return out << "dxt5";
}
nassertr(false, out);
return out << "(**invalid**)";
}
////////////////////////////////////////////////////////////////////
// Function: WrapMode output operator
// Description:

View File

@ -69,6 +69,10 @@ PUBLISHED:
F_red, F_green, F_blue, F_alpha, F_luminance,
F_luminance_alpha, F_luminance_alphamask
};
enum CompressionMode {
CM_default, CM_off, CM_on,
CM_fxt1, CM_dxt1, CM_dxt2, CM_dxt3, CM_dxt4, CM_dxt5,
};
enum WrapMode {
WM_unspecified, WM_clamp, WM_repeat,
WM_mirror, WM_mirror_once, WM_border_color
@ -157,6 +161,9 @@ PUBLISHED:
INLINE void set_format(Format format);
INLINE Format get_format() const;
INLINE void set_compression_mode(CompressionMode mode);
INLINE CompressionMode get_compression_mode() const;
INLINE void set_wrap_mode(WrapMode mode);
INLINE WrapMode get_wrap_mode() const;
@ -253,6 +260,7 @@ PUBLISHED:
static TextureType string_texture_type(const string &string);
static Format string_format(const string &string);
static CompressionMode string_compression_mode(const string &string);
static WrapMode string_wrap_mode(const string &string);
static FilterType string_filter_type(const string &string);
static EnvType string_env_type(const string &string);
@ -286,6 +294,7 @@ private:
TextureType _texture_type;
Format _format;
CompressionMode _compression_mode;
WrapMode _wrap_mode, _wrap_u, _wrap_v, _wrap_w;
FilterType _minfilter, _magfilter;
int _anisotropic_degree;
@ -367,6 +376,7 @@ INLINE ostream &operator << (ostream &out, const EggTexture &n) {
EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::TextureType texture_type);
EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::Format format);
EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::CompressionMode mode);
EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::WrapMode mode);
EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::FilterType type);
EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::EnvType type);

View File

@ -360,6 +360,14 @@ texture_body:
texture->set_format(f);
}
} else if (cmp_nocase_uh(name, "compression") == 0) {
EggTexture::CompressionMode w = EggTexture::string_compression_mode(strval);
if (w == EggTexture::CM_default) {
eggyywarning("Unknown texture compression mode " + strval);
} else {
texture->set_compression_mode(w);
}
} else if (cmp_nocase_uh(name, "wrap") == 0) {
EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval);
if (w == EggTexture::WM_unspecified) {

View File

@ -933,9 +933,23 @@ load_texture(TextureDef &def, const EggTexture *egg_tex) {
////////////////////////////////////////////////////////////////////
void EggLoader::
apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
tex->set_wrap_u(convert_wrap_mode(egg_tex->determine_wrap_u()));
tex->set_wrap_v(convert_wrap_mode(egg_tex->determine_wrap_v()));
tex->set_wrap_w(convert_wrap_mode(egg_tex->determine_wrap_w()));
if (egg_tex->get_compression_mode() != EggTexture::CM_default) {
tex->set_compression(convert_compression_mode(egg_tex->get_compression_mode()));
}
EggTexture::WrapMode wrap_u = egg_tex->determine_wrap_u();
EggTexture::WrapMode wrap_v = egg_tex->determine_wrap_v();
EggTexture::WrapMode wrap_w = egg_tex->determine_wrap_w();
if (wrap_u != EggTexture::WM_unspecified) {
tex->set_wrap_u(convert_wrap_mode(wrap_u));
}
if (wrap_v != EggTexture::WM_unspecified) {
tex->set_wrap_v(convert_wrap_mode(wrap_v));
}
if (wrap_w != EggTexture::WM_unspecified) {
tex->set_wrap_w(convert_wrap_mode(wrap_w));
}
if (egg_tex->has_border_color()) {
tex->set_border_color(egg_tex->get_border_color());
@ -1013,12 +1027,7 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
break;
case EggTexture::FT_unspecified:
// Default is bilinear, unless egg_ignore_filters is specified.
if (egg_ignore_filters) {
tex->set_minfilter(Texture::FT_nearest);
} else {
tex->set_minfilter(Texture::FT_linear);
}
break;
}
switch (egg_tex->get_magfilter()) {
@ -1041,12 +1050,7 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
break;
case EggTexture::FT_unspecified:
// Default is bilinear, unless egg_ignore_filters is specified.
if (egg_ignore_filters) {
tex->set_magfilter(Texture::FT_nearest);
} else {
tex->set_magfilter(Texture::FT_linear);
}
break;
}
if (egg_tex->has_anisotropic_degree()) {
@ -1176,6 +1180,50 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
}
}
////////////////////////////////////////////////////////////////////
// Function: EggLoader::convert_compression_mode
// Access: Private
// Description: Returns the Texture::CompressionMode enum
// corresponding to the EggTexture::CompressionMode.
// Returns CM_default if the compression mode is
// unspecified.
////////////////////////////////////////////////////////////////////
Texture::CompressionMode EggLoader::
convert_compression_mode(EggTexture::CompressionMode compression_mode) const {
switch (compression_mode) {
case EggTexture::CM_off:
return Texture::CM_off;
case EggTexture::CM_on:
return Texture::CM_on;
case EggTexture::CM_fxt1:
return Texture::CM_fxt1;
case EggTexture::CM_dxt1:
return Texture::CM_dxt1;
case EggTexture::CM_dxt2:
return Texture::CM_dxt2;
case EggTexture::CM_dxt3:
return Texture::CM_dxt3;
case EggTexture::CM_dxt4:
return Texture::CM_dxt4;
case EggTexture::CM_dxt5:
return Texture::CM_dxt5;
case EggTexture::CM_default:
return Texture::CM_default;
}
egg2pg_cat.warning()
<< "Unexpected texture compression flag: " << (int)compression_mode << "\n";
return Texture::CM_default;
}
////////////////////////////////////////////////////////////////////
// Function: EggLoader::convert_wrap_mode
// Access: Private

View File

@ -122,6 +122,7 @@ private:
void load_textures();
bool load_texture(TextureDef &def, const EggTexture *egg_tex);
void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex);
Texture::CompressionMode convert_compression_mode(EggTexture::CompressionMode compression_mode) const;
Texture::WrapMode convert_wrap_mode(EggTexture::WrapMode wrap_mode) const;
PT(TextureStage) make_texture_stage(const EggTexture *egg_tex);

View File

@ -593,12 +593,19 @@ load_model(const NodePath &parent, Filename filename) {
LoaderFileType *model_type =
reg->get_type_from_extension(extension);
if (model_type == (LoaderFileType *)NULL) {
// The extension isn't a known model file type, is it a known
// texture extension?
TexturePool *texture_pool = TexturePool::get_global_ptr();
if (texture_pool->get_texture_type(extension) != NULL) {
// It is a known texture extension.
is_image = true;
// The extension isn't a known model file type; is it a known
// image file extension?
if (extension == "txo") {
// A texture object. Not exactly an image, but certainly a
// texture.
is_image = true;
} else {
TexturePool *texture_pool = TexturePool::get_global_ptr();
if (texture_pool->get_texture_type(extension) != NULL) {
// It is a known image file extension.
is_image = true;
}
}
}
}
@ -1093,7 +1100,7 @@ load_image_as_model(const Filename &filename) {
int x_size = tex->get_x_size();
int y_size = tex->get_y_size();
bool has_alpha = false;
bool has_alpha = true;
LVecBase2f tex_scale(1.0f, 1.0f);
if (tex->is_of_type(VideoTexture::get_class_type())) {
@ -1103,7 +1110,7 @@ load_image_as_model(const Filename &filename) {
y_size = vtex->get_video_height();
tex_scale = vtex->get_tex_scale();
} else {
} else if (!tex->get_loaded_from_txo()) {
// Get the size from the original image (the texture may have
// scaled it to make a power of 2).
PNMImageHeader header;
@ -1127,7 +1134,6 @@ load_image_as_model(const Filename &filename) {
float top = y_size / 2.0;
PT(GeomNode) card_node = new GeomNode("card");
card_node->set_attrib(ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 1.0f, 1.0f)));
card_node->set_attrib(TextureAttrib::make(tex));
if (has_alpha) {
card_node->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));

File diff suppressed because it is too large Load Diff

View File

@ -65,6 +65,9 @@ typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset,
typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs);
typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data);
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data);
typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img);
class CLP(GeomContext);
@ -106,6 +109,7 @@ public:
virtual TextureContext *prepare_texture(Texture *tex);
virtual void release_texture(TextureContext *tc);
virtual bool extract_texture_data(Texture *tex);
virtual GeomContext *prepare_geom(Geom *geom);
virtual void release_geom(GeomContext *gc);
@ -236,14 +240,14 @@ protected:
static GLenum get_numeric_type(Geom::NumericType numeric_type);
GLenum get_texture_target(Texture::TextureType texture_type) const;
GLenum get_texture_wrap_mode(Texture::WrapMode wm);
GLenum get_texture_wrap_mode(Texture::WrapMode wm) const;
static Texture::WrapMode get_panda_wrap_mode(GLenum wm);
static GLenum get_texture_filter_type(Texture::FilterType ft, bool ignore_mipmaps);
static Texture::FilterType get_panda_filter_type(GLenum ft);
static GLenum get_component_type(Texture::ComponentType component_type);
GLint get_external_image_format(Texture::Format format) const;
static GLint get_internal_image_format(Texture::Format format);
static int get_external_texture_bytes(int width, int height, int depth,
GLint external_format,
GLenum component_type);
GLint get_external_image_format(Texture *tex) const;
GLint get_internal_image_format(Texture *tex) const;
static bool is_compressed_format(GLenum format);
static GLint get_texture_apply_mode_type(TextureStage::Mode am);
static GLint get_texture_combine_type(TextureStage::CombineMode cm);
GLint get_texture_src_type(TextureStage::CombineSource cs,
@ -268,7 +272,9 @@ protected:
GLenum target, GLint internal_format,
int width, int height, int depth,
GLint external_format, GLenum component_type,
const unsigned char *image);
const unsigned char *image,
size_t image_size,
Texture::CompressionMode image_compression);
void do_point_size();
@ -361,6 +367,10 @@ public:
PFNGLTEXIMAGE3DPROC _glTexImage3D;
PFNGLTEXSUBIMAGE3DPROC _glTexSubImage3D;
PFNGLCOMPRESSEDTEXIMAGE2DPROC _glCompressedTexImage2D;
PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC _glCompressedTexSubImage2D;
PFNGLGETCOMPRESSEDTEXIMAGEPROC _glGetCompressedTexImage;
bool _supports_bgr;
bool _supports_rescale_normal;

View File

@ -1,6 +1,7 @@
#define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
dtoolutil:c dtoolbase:c dtool:m prc:c
#define OSX_SYS_LIBS mx
#define USE_PACKAGES zlib
#begin lib_target
#define TARGET gobj

View File

@ -79,6 +79,13 @@ ConfigVariableBool keep_texture_ram
"texture image from disk; but it will consume memory somewhat "
"wastefully."));
ConfigVariableBool compressed_textures
("compressed-textures", false,
PRC_DESC("Set this to true to compress textures as they are loaded into "
"texture memory, if the driver supports this. Specifically, this "
"changes the meaning of set_compression(Texture::CM_default) to "
"Texture::CM_on."));
ConfigVariableBool vertex_buffers
("vertex-buffers", true,
PRC_DESC("Set this true to allow the use of vertex buffers (or buffer "
@ -167,11 +174,6 @@ ConfigVariableBool connect_triangle_strips
"triangle strip may help performance by reducing the number "
"of separate graphics calls that have to be made."));
ConfigVariableEnum<BamTextureMode> bam_texture_mode
("bam-texture-mode", BTM_relative,
PRC_DESC("Set this to specify how textures should be written into Bam files."
"See the panda source or documentation for available options."));
ConfigVariableEnum<AutoTextureScale> textures_power_2
("textures-power-2", ATS_down,
PRC_DESC("Specify whether textures should automatically be constrained to "
@ -300,52 +302,6 @@ ConfigureFn(config_gobj) {
InternalName::register_with_read_factory();
}
ostream &
operator << (ostream &out, BamTextureMode btm) {
switch (btm) {
case BTM_unchanged:
return out << "unchanged";
case BTM_fullpath:
return out << "fullpath";
case BTM_relative:
return out << "relative";
case BTM_basename:
return out << "basename";
case BTM_rawdata:
return out << "rawdata";
}
return out << "**invalid BamTextureMode (" << (int)btm << ")**";
}
istream &
operator >> (istream &in, BamTextureMode &btm) {
string word;
in >> word;
if (cmp_nocase(word, "unchanged") == 0) {
btm = BTM_unchanged;
} else if (cmp_nocase(word, "fullpath") == 0) {
btm = BTM_fullpath;
} else if (cmp_nocase(word, "relative") == 0) {
btm = BTM_relative;
} else if (cmp_nocase(word, "basename") == 0) {
btm = BTM_basename;
} else if (cmp_nocase(word, "rawdata") == 0) {
btm = BTM_rawdata;
} else {
gobj_cat->error() << "Invalid BamTextureMode value: " << word << "\n";
btm = BTM_relative;
}
return in;
}
ostream &
operator << (ostream &out, AutoTextureScale ats) {
switch (ats) {

View File

@ -28,16 +28,6 @@
NotifyCategoryDecl(gobj, EXPCL_PANDA, EXPTP_PANDA);
enum BamTextureMode {
BTM_unchanged,
BTM_fullpath,
BTM_relative,
BTM_basename,
BTM_rawdata
};
EXPCL_PANDA ostream &operator << (ostream &out, BamTextureMode btm);
EXPCL_PANDA istream &operator >> (istream &in, BamTextureMode &btm);
enum AutoTextureScale {
ATS_none,
ATS_down,
@ -49,6 +39,7 @@ EXPCL_PANDA istream &operator >> (istream &in, AutoTextureScale &ats);
// Configure variables for gobj package.
extern EXPCL_PANDA ConfigVariableInt max_texture_dimension;
extern EXPCL_PANDA ConfigVariableBool keep_texture_ram;
extern EXPCL_PANDA ConfigVariableBool compressed_textures;
extern EXPCL_PANDA ConfigVariableBool vertex_buffers;
extern EXPCL_PANDA ConfigVariableBool vertex_arrays;
extern EXPCL_PANDA ConfigVariableBool display_lists;
@ -58,7 +49,6 @@ extern EXPCL_PANDA ConfigVariableBool matrix_palette;
extern EXPCL_PANDA ConfigVariableBool display_list_animation;
extern EXPCL_PANDA ConfigVariableBool connect_triangle_strips;
extern EXPCL_PANDA ConfigVariableEnum<BamTextureMode> bam_texture_mode;
extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> textures_power_2;
extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> textures_square;
extern EXPCL_PANDA ConfigVariableBool textures_header_only;

View File

@ -398,13 +398,29 @@ get_border_color() const {
return _border_color;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_compression
// Access: Published
// Description: Returns the compression mode requested for this
// particular texture, or CM_off if the texture is not
// to be compressed.
//
// If a value other than CM_off is returned, this is
// not a guarantee that the texture is actually
// successfully compressed on the GSG. It may be that
// the GSG does not support the requested compression
// mode, in which case the texture may actually be
// stored uncompressed in texture memory.
////////////////////////////////////////////////////////////////////
INLINE Texture::CompressionMode Texture::
get_compression() const {
return _compression;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_render_to_texture
// Access: Published
// Description: Returns the solid color of the texture's border.
// Some OpenGL implementations use a border for tiling
// textures; in Panda, it is only used for specifying
// the clamp color.
// Description:
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
get_render_to_texture() const {
@ -446,7 +462,24 @@ might_have_ram_image() const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_image_size() const {
return _image.size();
return _ram_image.size();
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_ram_page_size
// Access: Published
// Description: Returns the number of bytes used by the in-memory
// image per page, or 0 if there is no in-memory image.
//
// For a non-compressed texture, this is the same as
// get_expected_ram_page_size(). For a compressed
// texture, this may be a smaller value. (We do assume
// that all pages will be the same size on a compressed
// texture).
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_page_size() const {
return _ram_image_compression != CM_off ? _ram_page_size : get_expected_ram_page_size();
}
////////////////////////////////////////////////////////////////////
@ -474,6 +507,21 @@ get_expected_ram_page_size() const {
return (size_t)(_x_size * _y_size * _num_components * _component_width);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_ram_image_compression
// Access: Published
// Description: Returns the compression mode in which the ram image
// is already stored pre-compressed. If this is other
// than CM_off, you cannot rely on the contents of the
// ram image to be anything predicatable (it will not be
// an array of x by y pixels, and it probably won't have
// the same length as get_expected_ram_image_size()).
////////////////////////////////////////////////////////////////////
INLINE Texture::CompressionMode Texture::
get_ram_image_compression() const {
return _ram_image_compression;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_keep_ram_image
// Access: Published
@ -678,6 +726,30 @@ get_loaded_from_disk() const {
return _loaded_from_disk;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_loaded_from_txo
// Access: Published
// Description: Sets the flag that indicates the texture has been
// loaded from a txo file. You probably shouldn't be
// setting this directly; it is set automatically when a
// Texture is loaded.
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_loaded_from_txo() {
_loaded_from_txo = true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_loaded_from_txo
// Access: Published
// Description: Returns the flag that indicates the texture has been
// loaded from a txo file.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
get_loaded_from_txo() const {
return _loaded_from_txo;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_match_framebuffer_format
// Access: Public
@ -725,7 +797,7 @@ set_match_framebuffer_format(bool flag) {
////////////////////////////////////////////////////////////////////
INLINE void Texture::
store_unscaled_byte(int &index, int value) {
_image[index++] = (uchar)value;
_ram_image[index++] = (uchar)value;
}
////////////////////////////////////////////////////////////////////
@ -744,8 +816,8 @@ store_unscaled_short(int &index, int value) {
uchar uc[2];
} v;
v.us = (ushort)value;
_image[index++] = v.uc[0];
_image[index++] = v.uc[1];
_ram_image[index++] = v.uc[0];
_ram_image[index++] = v.uc[1];
}
////////////////////////////////////////////////////////////////////
@ -786,8 +858,8 @@ store_scaled_short(int &index, int value, double scale) {
////////////////////////////////////////////////////////////////////
INLINE double Texture::
get_unsigned_byte(int &index) const {
nassertr(index >= 0 && index < (int)_image.size(), 0.0);
return (double)_image[index++] / 255.0;
nassertr(index >= 0 && index < (int)_ram_image.size(), 0.0);
return (double)_ram_image[index++] / 255.0;
}
////////////////////////////////////////////////////////////////////
@ -800,12 +872,29 @@ get_unsigned_byte(int &index) const {
////////////////////////////////////////////////////////////////////
INLINE double Texture::
get_unsigned_short(int &index) const {
nassertr(index >= 0 && index+1 < (int)_image.size(), 0.0);
nassertr(index >= 0 && index+1 < (int)_ram_image.size(), 0.0);
union {
ushort us;
uchar uc[2];
} v;
v.uc[0] = _image[index++];
v.uc[1] = _image[index++];
v.uc[0] = _ram_image[index++];
v.uc[1] = _ram_image[index++];
return (double)v.us / 65535.0;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::is_txo_filename
// Access: Private, Static
// Description: Returns true if the indicated filename ends in .txo
// or .txo.pz, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
is_txo_filename(const Filename &fullpath) {
string extension = fullpath.get_extension();
#ifdef HAVE_ZLIB
if (extension == "pz") {
extension = Filename(fullpath.get_basename_wo_extension()).get_extension();
}
#endif // HAVE_ZLIB
return (extension == "txo");
}

View File

@ -29,6 +29,11 @@
#include "preparedGraphicsObjects.h"
#include "pnmImage.h"
#include "virtualFileSystem.h"
#include "datagramInputFile.h"
#include "datagramOutputFile.h"
#include "bam.h"
#include "zStream.h"
#include "indent.h"
#include <stddef.h>
@ -58,6 +63,9 @@ Texture(const string &name) :
_keep_ram_image = true;
_all_dirty_flags = 0;
_border_color.set(0.0f, 0.0f, 0.0f, 1.0f);
_compression = CM_default;
_ram_image_compression = CM_off;
_ram_page_size = 0;
_render_to_texture = false;
_match_framebuffer_format = false;
@ -69,6 +77,7 @@ Texture(const string &name) :
set_component_type(T_unsigned_byte);
_loaded_from_disk = false;
_loaded_from_txo = false;
}
////////////////////////////////////////////////////////////////////
@ -95,6 +104,7 @@ Texture(const Texture &copy) :
_format(copy._format),
_component_type(copy._component_type),
_loaded_from_disk(copy._loaded_from_disk),
_loaded_from_txo(copy._loaded_from_txo),
_wrap_u(copy._wrap_u),
_wrap_v(copy._wrap_v),
_wrap_w(copy._wrap_w),
@ -103,12 +113,56 @@ Texture(const Texture &copy) :
_anisotropic_degree(copy._anisotropic_degree),
_keep_ram_image(copy._keep_ram_image),
_border_color(copy._border_color),
_compression(copy._compression),
_match_framebuffer_format(copy._match_framebuffer_format),
_all_dirty_flags(0),
_image(copy._image)
_ram_image(copy._ram_image),
_ram_image_compression(copy._ram_image_compression),
_ram_page_size(copy._ram_page_size)
{
}
////////////////////////////////////////////////////////////////////
// Function: Texture::Copy Assignment Operator
// Access: Protected
// Description: Use Texture::make_copy() to make a duplicate copy of
// an existing Texture.
////////////////////////////////////////////////////////////////////
void Texture::
operator = (const Texture &copy) {
Namable::operator = (copy);
_filename = copy._filename;
_alpha_filename = copy._alpha_filename;
_fullpath = copy._fullpath;
_alpha_fullpath = copy._alpha_fullpath;
_primary_file_num_channels = copy._primary_file_num_channels;
_alpha_file_channel = copy._alpha_file_channel;
_x_size = copy._x_size;
_y_size = copy._y_size;
_z_size = copy._z_size;
_num_components = copy._num_components;
_component_width = copy._component_width;
_texture_type = copy._texture_type;
_format = copy._format;
_component_type = copy._component_type;
_loaded_from_disk = copy._loaded_from_disk;
_loaded_from_txo = copy._loaded_from_txo;
_wrap_u = copy._wrap_u;
_wrap_v = copy._wrap_v;
_wrap_w = copy._wrap_w;
_minfilter = copy._minfilter;
_magfilter = copy._magfilter;
_anisotropic_degree = copy._anisotropic_degree;
_keep_ram_image = copy._keep_ram_image;
_border_color = copy._border_color;
_compression = copy._compression;
_match_framebuffer_format = copy._match_framebuffer_format;
_all_dirty_flags = 0;
_ram_image = copy._ram_image;
_ram_image_compression = copy._ram_image_compression;
_ram_page_size = copy._ram_page_size;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::Destructor
// Access: Published, Virtual
@ -170,6 +224,7 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
clear_ram_image();
_loaded_from_disk = false;
_loaded_from_txo = false;
}
////////////////////////////////////////////////////////////////////
@ -187,7 +242,7 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
void Texture::
generate_normalization_cube_map(int size) {
setup_cube_map(size, T_unsigned_byte, F_rgb);
PTA_uchar image = modify_ram_image();
PTA_uchar image = make_ram_image();
float half_size = (float)size * 0.5f;
float center = half_size - 0.5f;
@ -284,9 +339,18 @@ generate_normalization_cube_map(int size) {
// than this number.
//
// This also implicitly sets keep_ram_image to false.
//
// If the filename has the extension .txo, this
// implicitly reads a texture object instead of a
// filename (which replaces all of the texture
// properties).
////////////////////////////////////////////////////////////////////
bool Texture::
read(const Filename &fullpath, int z, int primary_file_num_channels) {
if (is_txo_filename(fullpath)) {
return read_txo_file(fullpath);
}
PNMImage image;
if (textures_header_only) {
@ -461,11 +525,24 @@ read(const Filename &fullpath, const Filename &alpha_fullpath,
////////////////////////////////////////////////////////////////////
// Function: Texture::write
// Access: Published
// Description: Writes the texture to the indicated filename.
// Description: Writes the texture to the indicated filename. If the
// filename ends in the extension .txo, this implicitly
// writes a Panda texture object (.txo) instead of an
// image file.
////////////////////////////////////////////////////////////////////
bool Texture::
write(const Filename &name, int z) const {
if (!has_ram_image()) {
((Texture *)this)->get_ram_image();
}
nassertr(has_ram_image(), false);
if (is_txo_filename(name)) {
return write_txo_file(name);
}
nassertr(get_ram_image_compression() == CM_off, false);
PNMImage pnmimage;
if (!store(pnmimage, z)) {
return false;
@ -479,6 +556,114 @@ write(const Filename &name, int z) const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read_txo
// Access: Published
// Description: Reads the texture from a Panda texture object. This
// defines the complete Texture specification, including
// the image data as well as all texture properties.
//
// The filename is just for reference.
////////////////////////////////////////////////////////////////////
bool Texture::
read_txo(istream &in, const string &filename) {
DatagramInputFile din;
if (!din.open(in)) {
gobj_cat.error()
<< "Could not read texture object: " << filename << "\n";
return false;
}
string head;
if (!din.read_header(head, _bam_header.size())) {
gobj_cat.error()
<< filename << " is not a texture object file.\n";
return false;
}
if (head != _bam_header) {
gobj_cat.error()
<< filename << " is not a texture object file.\n";
return false;
}
BamReader reader(&din, filename);
if (!reader.init()) {
return false;
}
TypedWritable *object = reader.read_object();
if (object == (TypedWritable *)NULL) {
gobj_cat.error()
<< "Texture object " << filename << " is empty.\n";
return false;
} else if (!object->is_of_type(Texture::get_class_type())) {
gobj_cat.error()
<< "Texture object " << filename << "contains a "
<< object->get_type() << ", not a Texture.\n";
return false;
}
PT(Texture) other = DCAST(Texture, object);
if (!reader.resolve()) {
gobj_cat.error()
<< "Unable to fully resolve texture object file.\n";
return false;
}
(*this) = (*other);
_loaded_from_disk = true;
_loaded_from_txo = true;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::write_txo
// Access: Published
// Description: Writes the texture to a Panda texture object. This
// defines the complete Texture specification, including
// the image data as well as all texture properties.
//
// The filename is just for reference.
////////////////////////////////////////////////////////////////////
bool Texture::
write_txo(ostream &out, const string &filename) const {
DatagramOutputFile dout;
if (!dout.open(out)) {
gobj_cat.error()
<< "Could not write texture object: " << filename << "\n";
return false;
}
if (!dout.write_header(_bam_header)) {
gobj_cat.error()
<< "Unable to write to " << filename << "\n";
return false;
}
BamWriter writer(&dout, filename);
if (!writer.init()) {
return false;
}
writer.set_file_texture_mode(BTM_rawdata);
if (!writer.write_object(this)) {
return false;
}
if (!has_ram_image()) {
gobj_cat.error()
<< get_name() << " does not have ram image\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read_pages
// Access: Published
@ -497,6 +682,10 @@ write(const Filename &name, int z) const {
////////////////////////////////////////////////////////////////////
bool Texture::
read_pages(Filename fullpath_pattern, int z_size) {
if (is_txo_filename(fullpath_pattern)) {
return read_txo_file(fullpath_pattern);
}
fullpath_pattern.set_pattern(true);
if (!fullpath_pattern.has_hash()) {
gobj_cat.error()
@ -566,6 +755,10 @@ read_pages(Filename fullpath_pattern, int z_size) {
////////////////////////////////////////////////////////////////////
bool Texture::
write_pages(Filename fullpath_pattern) {
if (is_txo_filename(fullpath_pattern)) {
return write_txo_file(fullpath_pattern);
}
fullpath_pattern.set_pattern(true);
if (!fullpath_pattern.has_hash()) {
gobj_cat.error()
@ -938,13 +1131,37 @@ set_border_color(const Colorf &color) {
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_compression
// Access: Published
// Description: Requests that this particular Texture be compressed
// when it is loaded into texture memory.
//
// This refers to the internal compression of the
// texture image within texture memory; it is not
// related to jpeg or png compression, which are disk
// file compression formats. The actual disk file that
// generated this texture may be stored in a compressed
// or uncompressed format supported by Panda; it will be
// decompressed on load, and then recompressed by the
// graphics API if this parameter is not CM_off.
//
// If the GSG does not support this texture compression
// mode, the texture will silently be loaded
// uncompressed.
////////////////////////////////////////////////////////////////////
void Texture::
set_compression(Texture::CompressionMode compression) {
if (_compression != compression) {
mark_dirty(DF_image);
_compression = compression;
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_render_to_texture
// Access: Published
// Description: Specifies the solid color of the texture's border.
// Some OpenGL implementations use a border for tiling
// textures; in Panda, it is only used for specifying
// the clamp color.
// Description:
////////////////////////////////////////////////////////////////////
void Texture::
set_render_to_texture(bool render_to_texture) {
@ -981,7 +1198,7 @@ set_render_to_texture(bool render_to_texture) {
////////////////////////////////////////////////////////////////////
bool Texture::
has_ram_image() const {
return !_image.empty();
return !_ram_image.empty();
}
////////////////////////////////////////////////////////////////////
@ -1018,26 +1235,28 @@ get_ram_image() {
reload_ram_image();
}
return _image;
return _ram_image;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::modify_ram_image
// Access: Published
// Description: Returns a modifiable pointer to the system-RAM image.
// If the RAM image has been dumped, creates a new one.
// This assumes the RAM image should be uncompressed.
// If the RAM image has been dumped, or is stored
// compressed, creates a new one.
//
// This also implicitly sets keep_ram_image to true.
////////////////////////////////////////////////////////////////////
PTA_uchar Texture::
modify_ram_image() {
if (_image.empty()) {
if (_ram_image.empty() || _ram_image_compression != CM_off) {
make_ram_image();
}
mark_dirty(DF_image);
_keep_ram_image = true;
return _image;
return _ram_image;
}
////////////////////////////////////////////////////////////////////
@ -1051,25 +1270,35 @@ modify_ram_image() {
////////////////////////////////////////////////////////////////////
PTA_uchar Texture::
make_ram_image() {
_image = PTA_uchar::empty_array(get_expected_ram_image_size());
_ram_image = PTA_uchar::empty_array(get_expected_ram_image_size());
_ram_image_compression = CM_off;
mark_dirty(DF_image);
_keep_ram_image = true;
return _image;
return _ram_image;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_ram_image
// Access: Published
// Description: Replaces the current system-RAM image with the new
// data.
// data. If compression is not CM_off, it indicates
// that the new data is already pre-compressed in the
// indicated format.
//
// This also implicitly sets keep_ram_image to true.
////////////////////////////////////////////////////////////////////
void Texture::
set_ram_image(PTA_uchar image) {
nassertv(image.size() == get_expected_ram_image_size());
if (_image != image) {
_image = image;
set_ram_image(PTA_uchar image, Texture::CompressionMode compression,
size_t page_size) {
nassertv(compression != CM_default);
nassertv(compression != CM_off || image.size() == get_expected_ram_image_size());
if (_ram_image != image || _ram_image_compression != compression) {
_ram_image = image;
_ram_image_compression = compression;
_ram_page_size = page_size;
if (page_size == 0) {
_ram_page_size = image.size();
}
mark_dirty(DF_image);
}
_keep_ram_image = true;
@ -1082,7 +1311,8 @@ set_ram_image(PTA_uchar image) {
////////////////////////////////////////////////////////////////////
void Texture::
clear_ram_image() {
_image.clear();
_ram_image.clear();
_ram_image_compression = CM_off;
}
////////////////////////////////////////////////////////////////////
@ -1172,6 +1402,169 @@ release_all() {
return num_freed;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::write
// Access: Published
// Description: Not to be confused with write(Filename), this method
// simply describes the texture properties.
////////////////////////////////////////////////////////////////////
void Texture::
write(ostream &out, int indent_level) const {
indent(out, indent_level)
<< get_type() << " " << get_name();
if (!get_filename().empty()) {
out << " (from " << get_filename() << ")";
}
out << "\n";
indent(out, indent_level + 2);
switch (get_texture_type()) {
case TT_1d_texture:
out << "1-d, " << get_x_size();
break;
case TT_2d_texture:
out << "2-d, " << get_x_size() << " x " << get_y_size();
break;
case TT_3d_texture:
out << "3-d, " << get_x_size() << " x " << get_y_size()
<< " x " << get_z_size();
break;
case TT_cube_map:
out << "cube map, " << get_x_size() << " x " << get_y_size();
break;
}
out << " pixels, each " << get_num_components();
switch (get_component_type()) {
case T_unsigned_byte:
out << " bytes";
break;
case T_unsigned_short:
out << " shorts";
break;
case T_float:
out << " floats";
break;
}
out << ", ";
switch (get_format()) {
case F_color_index:
out << "color_index";
break;
case F_stencil_index:
out << "stencil_index";
break;
case F_depth_component:
out << "depth_component";
break;
case F_rgba:
out << "rgba";
break;
case F_rgbm:
out << "rgbm";
break;
case F_rgba12:
out << "rgba12";
break;
case F_rgba8:
out << "rgba8";
break;
case F_rgba4:
out << "rgba4";
break;
case F_rgb:
out << "rgb";
break;
case F_rgb12:
out << "rgb12";
break;
case F_rgb8:
out << "rgb8";
break;
case F_rgb5:
out << "rgb5";
break;
case F_rgba5:
out << "rgba5";
break;
case F_rgb332:
out << "rgb332";
break;
case F_red:
out << "red";
break;
case F_green:
out << "green";
break;
case F_blue:
out << "blue";
break;
case F_alpha:
out << "alpha";
break;
case F_luminance:
out << "luminance";
break;
case F_luminance_alpha:
out << "luminance_alpha";
break;
case F_luminance_alphamask:
out << "luminance_alphamask";
break;
}
if (get_compression() != CM_default) {
out << ", compression " << get_compression();
}
out << "\n";
indent(out, indent_level + 2);
switch (get_texture_type()) {
case TT_1d_texture:
out << get_wrap_u() << ", ";
break;
case TT_2d_texture:
out << get_wrap_u() << " x " << get_wrap_v() << ", ";
break;
case TT_3d_texture:
out << get_wrap_u() << " x " << get_wrap_v()
<< " x " << get_wrap_w() << ", ";
break;
case TT_cube_map:
break;
}
out << "min " << get_minfilter()
<< ", mag " << get_magfilter()
<< ", aniso " << get_anisotropic_degree()
<< ", border " << get_border_color()
<< "\n";
indent(out, indent_level + 2);
if (has_ram_image()) {
out << get_ram_image_size() << " bytes in ram, compression "
<< get_ram_image_compression() << "\n";
} else {
out << "no ram image\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_format
// Access: Published
@ -1307,7 +1700,7 @@ prepare_now(PreparedGraphicsObjects *prepared_objects,
gobj_cat.debug()
<< "Dumping RAM for texture " << get_name() << "\n";
}
_image.clear();
_ram_image.clear();
}
}
return tc;
@ -1465,17 +1858,23 @@ reconsider_dirty() {
////////////////////////////////////////////////////////////////////
void Texture::
reload_ram_image() {
if (_texture_type == TT_1d_texture || _texture_type == TT_2d_texture) {
gobj_cat.info()
<< "Reloading texture " << get_name() << "\n";
gobj_cat.info()
<< "Reloading texture " << get_name() << "\n";
make_ram_image();
make_ram_image();
if (_texture_type == TT_1d_texture || _texture_type == TT_2d_texture) {
// 1-d or 2-d texture. Just one page.
if (has_alpha_fullpath()) {
read(get_fullpath(), get_alpha_fullpath(),
0, _primary_file_num_channels, _alpha_file_channel);
} else {
read(get_fullpath(), 0, _primary_file_num_channels);
}
} else {
// 3-d texture or cube map. Multiple pages.
read_pages(get_fullpath(), _z_size);
}
}
@ -1510,6 +1909,50 @@ down_to_power_2(int value) {
return x;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::is_specific
// Access: Public, Static
// Description: Returns true if the indicated compression mode is one
// of the specific compression types, false otherwise.
////////////////////////////////////////////////////////////////////
bool Texture::
is_specific(Texture::CompressionMode compression) {
switch (compression) {
case CM_default:
case CM_off:
case CM_on:
return false;
default:
return true;
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::has_alpha
// Access: Public, Static
// Description: Returns true if the indicated format includes alpha,
// false otherwise.
////////////////////////////////////////////////////////////////////
bool Texture::
has_alpha(Format format) {
switch (format) {
case F_alpha:
case F_rgba:
case F_rgbm:
case F_rgba4:
case F_rgba5:
case F_rgba8:
case F_rgba12:
case F_luminance_alpha:
case F_luminance_alphamask:
return true;
default:
return false;
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::reconsider_z_size
// Access: Protected
@ -1530,9 +1973,9 @@ reconsider_z_size(int z) {
// Increase the size of the data buffer to make room for the new
// texture level.
size_t new_size = get_expected_ram_image_size();
if (!_image.is_null() && new_size > _image.size()) {
_image.insert(_image.end(), new_size - _image.size(), 0);
nassertr(_image.size() == new_size, false);
if (!_ram_image.is_null() && new_size > _ram_image.size()) {
_ram_image.insert(_ram_image.end(), new_size - _ram_image.size(), 0);
nassertr(_ram_image.size() == new_size, false);
}
}
@ -1710,6 +2153,66 @@ consider_downgrade(PNMImage &pnmimage, int num_channels) {
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read_txo_file
// Access: Private
// Description: Called internally when read() or read_pages() detects
// a txo file.
////////////////////////////////////////////////////////////////////
bool Texture::
read_txo_file(const Filename &fullpath) {
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
Filename filename = Filename::binary_filename(fullpath);
PT(VirtualFile) file = vfs->get_file(filename);
if (file == (VirtualFile *)NULL) {
// No such file.
gobj_cat.error()
<< "Could not find " << fullpath << "\n";
return false;
}
if (gobj_cat.is_debug()) {
gobj_cat.debug()
<< "Reading texture object " << filename << "\n";
}
istream *in = file->open_read_file(true);
bool success = read_txo(*in, fullpath);
vfs->close_read_file(in);
set_fullpath(fullpath);
clear_alpha_fullpath();
_keep_ram_image = false;
return success;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::write_txo_file
// Access: Private
// Description: Called internally when write() or write_pages() detects
// a txo file.
////////////////////////////////////////////////////////////////////
bool Texture::
write_txo_file(const Filename &fullpath) const {
Filename filename = Filename::binary_filename(fullpath);
ofstream out;
if (!filename.open_write(out)) {
gobj_cat.error()
<< "Unable to open " << filename << "\n";
return false;
}
#ifdef HAVE_ZLIB
if (fullpath.get_extension() == "pz") {
OCompressStream compressor(&out, false);
return write_txo(compressor);
}
#endif // HAVE_ZLIB
return write_txo(out, fullpath);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::register_with_read_factory
// Access: Public, Static
@ -1752,7 +2255,12 @@ make_from_bam(const FactoryParams &params) {
if (has_rawdata) {
// If the raw image data is included, then just create a Texture
// and don't load from the file.
me = new Texture("");
me = new Texture(name);
me->_filename = filename;
me->_alpha_filename = alpha_filename;
me->_primary_file_num_channels = primary_file_num_channels;
me->_alpha_file_channel = alpha_file_channel;
me->_texture_type = texture_type;
} else {
if (filename.empty()) {
@ -1828,6 +2336,10 @@ fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
_magfilter = (FilterType)scan.get_uint8();
_anisotropic_degree = scan.get_int16();
_border_color.read_datagram(scan);
_compression = CM_default;
if (manager->get_file_minor_ver() >= 1) {
_compression = (CompressionMode)scan.get_uint8();
}
Format format = (Format)scan.get_uint8();
int num_components = scan.get_uint8();
@ -1849,15 +2361,21 @@ fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
_z_size = scan.get_uint32();
_component_type = (ComponentType)scan.get_uint8();
_component_width = scan.get_uint8();
_ram_image_compression = CM_off;
_ram_page_size = 0;
if (manager->get_file_minor_ver() >= 1) {
_ram_image_compression = (CompressionMode)scan.get_uint8();
_ram_page_size = scan.get_uint32();
}
_loaded_from_disk = false;
PN_uint32 u_size = scan.get_uint32();
// fill the _image buffer with image data
string temp_buff = scan.extract_bytes(u_size);
_image = PTA_uchar::empty_array((int) u_size);
_ram_image = PTA_uchar::empty_array((int) u_size);
for (PN_uint32 u_idx=0; u_idx < u_size; ++u_idx) {
_image[(int)u_idx] = (uchar) temp_buff[u_idx];
_ram_image[(int)u_idx] = (uchar) temp_buff[u_idx];
}
}
}
@ -1870,11 +2388,6 @@ fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
////////////////////////////////////////////////////////////////////
void Texture::
write_datagram(BamWriter *manager, Datagram &me) {
bool has_bam_dir = !manager->get_filename().empty();
Filename bam_dir = manager->get_filename().get_dirname();
Filename filename = get_filename();
Filename alpha_filename = get_alpha_filename();
// Write out the texture's raw pixel data if (a) the current Bam
// Texture Mode requires that, or (b) there's no filename, so the
// file can't be loaded up from disk, but the raw pixel data is
@ -1883,10 +2396,24 @@ write_datagram(BamWriter *manager, Datagram &me) {
// Otherwise, we just write out the filename, and assume whoever
// loads the bam file later will have access to the image file on
// disk.
BamTextureMode file_texture_mode = manager->get_file_texture_mode();
bool has_rawdata =
(bam_texture_mode == BTM_rawdata || (has_ram_image() && filename.empty()));
(file_texture_mode == BTM_rawdata || (has_ram_image() && get_filename().empty()));
if (has_rawdata && !has_ram_image()) {
get_ram_image();
if (!has_ram_image()) {
// No image data after all.
has_rawdata = false;
}
}
switch (bam_texture_mode) {
bool has_bam_dir = !manager->get_filename().empty();
Filename bam_dir = manager->get_filename().get_dirname();
Filename filename = get_filename();
Filename alpha_filename = get_alpha_filename();
switch (file_texture_mode) {
case BTM_unchanged:
case BTM_rawdata:
break;
@ -1928,7 +2455,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
default:
gobj_cat.error()
<< "Unsupported bam-texture-mode: " << (int)bam_texture_mode << "\n";
<< "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n";
}
me.add_string(get_name());
@ -1947,6 +2474,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
me.add_uint8(_magfilter);
me.add_int16(_anisotropic_degree);
_border_color.write_datagram(me);
me.add_uint8(_compression);
me.add_uint8(_format);
me.add_uint8(_num_components);
@ -1959,9 +2487,11 @@ write_datagram(BamWriter *manager, Datagram &me) {
me.add_uint32(_z_size);
me.add_uint8(_component_type);
me.add_uint8(_component_width);
me.add_uint8(_ram_image_compression);
me.add_uint32(_ram_page_size);
me.add_uint32(_image.size());
me.append_data(_image, _image.size());
me.add_uint32(_ram_image.size());
me.append_data(_ram_image, _ram_image.size());
}
}
@ -2046,3 +2576,33 @@ operator >> (istream &in, Texture::WrapMode &wm) {
wm = Texture::string_wrap_mode(word);
return in;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::CompressionMode output operator
// Description:
////////////////////////////////////////////////////////////////////
ostream &
operator << (ostream &out, Texture::CompressionMode cm) {
switch (cm) {
case Texture::CM_default:
return out << "default";
case Texture::CM_off:
return out << "off";
case Texture::CM_on:
return out << "on";
case Texture::CM_fxt1:
return out << "fxt1";
case Texture::CM_dxt1:
return out << "dxt1";
case Texture::CM_dxt2:
return out << "dxt2";
case Texture::CM_dxt3:
return out << "dxt3";
case Texture::CM_dxt4:
return out << "dxt4";
case Texture::CM_dxt5:
return out << "dxt5";
}
return out << "(**invalid Texture::CompressionMode(" << (int)cm << ")**)";
}

View File

@ -144,10 +144,31 @@ PUBLISHED:
WM_invalid
};
enum CompressionMode {
// Generic compresssion modes. Usually, you should choose one of
// these.
CM_default, // on or off, according to compressed-textures
CM_off, // uncompressed image
CM_on, // whatever compression the driver supports
// Specific compression modes. Use only when you really want to
// use a particular compression algorithm. Use with caution; not
// all drivers support all compression modes. You can use
// 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,
};
PUBLISHED:
Texture(const string &name = string());
protected:
Texture(const Texture &copy);
void operator = (const Texture &copy);
PUBLISHED:
virtual ~Texture();
@ -179,6 +200,9 @@ PUBLISHED:
int primary_file_num_channels = 0, int alpha_file_channel = 0);
bool write(const Filename &fullpath, int z = 0) const;
bool read_txo(istream &in, const string &filename = "stream");
bool write_txo(ostream &out, const string &filename = "stream") const;
bool read_pages(Filename fullpath_pattern, int z_size = 0);
bool write_pages(Filename fullpath_pattern);
@ -213,6 +237,7 @@ PUBLISHED:
void set_magfilter(FilterType filter);
void set_anisotropic_degree(int anisotropic_degree);
void set_border_color(const Colorf &color);
void set_compression(CompressionMode compression);
void set_render_to_texture(bool render_to_texture);
INLINE WrapMode get_wrap_u() const;
@ -222,18 +247,22 @@ PUBLISHED:
INLINE FilterType get_magfilter() const;
INLINE int get_anisotropic_degree() const;
INLINE Colorf get_border_color() const;
INLINE CompressionMode get_compression() const;
INLINE bool get_render_to_texture() const;
INLINE bool uses_mipmaps() const;
virtual bool has_ram_image() const;
INLINE bool might_have_ram_image() const;
INLINE size_t get_ram_image_size() const;
INLINE size_t get_ram_page_size() const;
INLINE size_t get_expected_ram_image_size() const;
INLINE size_t get_expected_ram_page_size() const;
CPTA_uchar get_ram_image();
INLINE CompressionMode get_ram_image_compression() const;
PTA_uchar modify_ram_image();
PTA_uchar make_ram_image();
void set_ram_image(PTA_uchar image);
void set_ram_image(PTA_uchar image, CompressionMode compression = CM_off,
size_t page_size = 0);
void clear_ram_image();
INLINE void set_keep_ram_image(bool keep_ram_image);
virtual bool get_keep_ram_image() const;
@ -242,6 +271,8 @@ PUBLISHED:
bool release(PreparedGraphicsObjects *prepared_objects);
int release_all();
void write(ostream &out, int indent_level) const;
PUBLISHED:
// These are published, but in general, you shouldn't be mucking
// with these values; they are set automatically when a texture is
@ -264,6 +295,9 @@ PUBLISHED:
INLINE void set_loaded_from_disk();
INLINE bool get_loaded_from_disk() const;
INLINE void set_loaded_from_txo();
INLINE bool get_loaded_from_txo() const;
public:
INLINE bool get_match_framebuffer_format() const;
INLINE void set_match_framebuffer_format(bool flag);
@ -298,6 +332,9 @@ public:
static int up_to_power_2(int value);
static int down_to_power_2(int value);
static bool is_specific(CompressionMode compression);
static bool has_alpha(Format format);
protected:
virtual void reconsider_dirty();
virtual void reload_ram_image();
@ -319,6 +356,10 @@ private:
INLINE double get_unsigned_byte(int &index) const;
INLINE double get_unsigned_short(int &index) const;
INLINE static bool is_txo_filename(const Filename &fullpath);
bool read_txo_file(const Filename &fullpath);
bool write_txo_file(const Filename &fullpath) const;
protected:
Filename _filename;
Filename _alpha_filename;
@ -344,6 +385,7 @@ protected:
ComponentType _component_type;
bool _loaded_from_disk;
bool _loaded_from_txo;
WrapMode _wrap_u;
WrapMode _wrap_v;
@ -353,6 +395,7 @@ protected:
int _anisotropic_degree;
bool _keep_ram_image;
Colorf _border_color;
CompressionMode _compression;
bool _render_to_texture;
bool _match_framebuffer_format;
@ -377,7 +420,9 @@ protected:
// texture.
int _all_dirty_flags;
PTA_uchar _image;
PTA_uchar _ram_image;
CompressionMode _ram_image_compression;
size_t _ram_page_size;
// Datagram stuff
@ -417,6 +462,8 @@ EXPCL_PANDA istream &operator >> (istream &in, Texture::FilterType &ft);
EXPCL_PANDA ostream &operator << (ostream &out, Texture::WrapMode wm);
EXPCL_PANDA istream &operator >> (istream &in, Texture::WrapMode &wm);
EXPCL_PANDA ostream &operator << (ostream &out, Texture::CompressionMode cm);
#include "texture.I"
#endif

View File

@ -33,6 +33,9 @@ VideoTexture::
VideoTexture(const string &name) :
Texture(name)
{
// We don't want to try to compress each frame as it's loaded.
_compression = CM_off;
_video_width = 0;
_video_height = 0;
@ -69,7 +72,7 @@ has_ram_image() const {
if (this_frame != _last_frame_update) {
return false;
}
return !_image.empty();
return !_ram_image.empty();
}
////////////////////////////////////////////////////////////////////

View File

@ -346,7 +346,7 @@ update_frame(int frame) {
const unsigned char *source = page._color.get_frame_data(frame);
if (source != NULL) {
nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size);
unsigned char *dest = _image.p() + get_expected_ram_page_size() * z;
unsigned char *dest = _ram_image.p() + get_expected_ram_page_size() * z;
int dest_row_width = (_x_size * _num_components * _component_width);
int source_row_width = get_video_width() * 3;
@ -385,7 +385,7 @@ update_frame(int frame) {
const unsigned char *source = page._alpha.get_frame_data(frame);
if (source != NULL) {
nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size);
unsigned char *dest = _image.p() + get_expected_ram_page_size() * z;
unsigned char *dest = _ram_image.p() + get_expected_ram_page_size() * z;
int dest_row_width = (_x_size * _num_components * _component_width);
int source_row_width = get_video_width() * 3;

View File

@ -369,6 +369,30 @@ get_current_minor_ver() {
return _bam_minor_ver;
}
////////////////////////////////////////////////////////////////////
// Function: BamFile::get_reader
// Access: Public
// Description: Returns the BamReader in charge of performing the
// read operations. This will return NULL unless
// open_read() was called.
////////////////////////////////////////////////////////////////////
BamReader *BamFile::
get_reader() {
return _reader;
}
////////////////////////////////////////////////////////////////////
// Function: BamFile::get_writer
// Access: Public
// Description: Returns the BamWriter in charge of performing the
// write operations. This will return NULL unless
// open_write() was called.
////////////////////////////////////////////////////////////////////
BamWriter *BamFile::
get_writer() {
return _writer;
}
////////////////////////////////////////////////////////////////////
// Function: BamFile::continue_open_read
// Access: Private

View File

@ -78,6 +78,9 @@ PUBLISHED:
int get_current_major_ver();
int get_current_minor_ver();
BamReader *get_reader();
BamWriter *get_writer();
private:
bool continue_open_read(const string &bam_filename, bool report_errors);
bool continue_open_write(const string &bam_filename, bool report_errors);

View File

@ -12,7 +12,9 @@
animInterface.h animInterface.I \
bam.h bamEndian.h \
bamReader.I bamReader.N bamReader.h bamReaderParam.I \
bamReaderParam.h bamWriter.I bamWriter.h \
bamReaderParam.h \
bamTextureMode.h \
bamWriter.I bamWriter.h \
bitArray.I bitArray.h \
bitMask.I bitMask.h \
buttonHandle.I \
@ -58,7 +60,9 @@
#define INCLUDED_SOURCES \
animInterface.cxx \
bamEndian.cxx \
bamReader.cxx bamReaderParam.cxx bamWriter.cxx \
bamReader.cxx bamReaderParam.cxx \
bamTextureMode.cxx \
bamWriter.cxx \
bitArray.cxx \
bitMask.cxx \
buttonHandle.cxx buttonRegistry.cxx \
@ -91,6 +95,7 @@
animInterface.h animInterface.I \
bam.h bamEndian.h \
bamReader.I bamReader.h bamReaderParam.I bamReaderParam.h \
bamTextureMode.h \
bamWriter.I bamWriter.h \
bitArray.I bitArray.h \
bitMask.I bitMask.h \

View File

@ -36,7 +36,8 @@ static const unsigned short _bam_major_ver = 6;
// Bumped to major version 5 on 5/6/05 for new Geom implementation.
// Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
static const unsigned short _bam_minor_ver = 0;
static const unsigned short _bam_minor_ver = 1;
// Bumped to minor version 1 on 3/12/06 to add Texture::_compression.
#endif

View File

@ -0,0 +1,66 @@
// Filename: bamTextureMode.cxx
// Created by: drose (14Mar06)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "bamTextureMode.h"
#include "config_util.h"
ostream &
operator << (ostream &out, BamTextureMode btm) {
switch (btm) {
case BTM_unchanged:
return out << "unchanged";
case BTM_fullpath:
return out << "fullpath";
case BTM_relative:
return out << "relative";
case BTM_basename:
return out << "basename";
case BTM_rawdata:
return out << "rawdata";
}
return out << "**invalid BamTextureMode (" << (int)btm << ")**";
}
istream &
operator >> (istream &in, BamTextureMode &btm) {
string word;
in >> word;
if (cmp_nocase(word, "unchanged") == 0) {
btm = BTM_unchanged;
} else if (cmp_nocase(word, "fullpath") == 0) {
btm = BTM_fullpath;
} else if (cmp_nocase(word, "relative") == 0) {
btm = BTM_relative;
} else if (cmp_nocase(word, "basename") == 0) {
btm = BTM_basename;
} else if (cmp_nocase(word, "rawdata") == 0) {
btm = BTM_rawdata;
} else {
util_cat->error() << "Invalid BamTextureMode value: " << word << "\n";
btm = BTM_relative;
}
return in;
}

View File

@ -0,0 +1,36 @@
// Filename: bamTextureMode.h
// Created by: drose (14Mar06)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef BAMTEXTUREMODE_H
#define BAMTEXTUREMODE_H
#include "pandabase.h"
// This enum is used to control how textures are written to a bam
// stream.
enum BamTextureMode {
BTM_unchanged,
BTM_fullpath,
BTM_relative,
BTM_basename,
BTM_rawdata
};
EXPCL_PANDA ostream &operator << (ostream &out, BamTextureMode btm);
EXPCL_PANDA istream &operator >> (istream &in, BamTextureMode &btm);
#endif

View File

@ -43,3 +43,29 @@ INLINE BamEndian BamWriter::
get_file_endian() const {
return _file_endian;
}
////////////////////////////////////////////////////////////////////
// Function: BamWriter::get_file_texture_mode
// Access: Public
// Description: Returns the BamTextureMode preference indicated by
// the Bam file currently being written. Texture
// objects written to this Bam file will be encoded
// according to the specified mode.
////////////////////////////////////////////////////////////////////
INLINE BamTextureMode BamWriter::
get_file_texture_mode() const {
return _file_texture_mode;
}
////////////////////////////////////////////////////////////////////
// Function: BamWriter::set_file_texture_mode
// Access: Public
// Description: Changes the BamTextureMode preference for
// the Bam file currently being written. Texture
// objects written to this Bam file will be encoded
// according to the specified mode.
////////////////////////////////////////////////////////////////////
INLINE void BamWriter::
set_file_texture_mode(BamTextureMode file_texture_mode) {
_file_texture_mode = file_texture_mode;
}

View File

@ -78,6 +78,7 @@ init() {
_long_pta_id = false;
_file_endian = bam_endian;
_file_texture_mode = bam_texture_mode;
// Write out the current major and minor BAM file version numbers.
Datagram header;

View File

@ -22,6 +22,7 @@
#include "pandabase.h"
#include "notify.h"
#include "bamEndian.h"
#include "bamTextureMode.h"
#include "typedWritable.h"
#include "datagramSink.h"
#include "pdeque.h"
@ -88,6 +89,9 @@ public:
INLINE BamEndian get_file_endian() const;
INLINE BamTextureMode get_file_texture_mode() const;
INLINE void set_file_texture_mode(BamTextureMode file_texture_mode);
public:
// Functions to support classes that write themselves to the Bam.
@ -109,6 +113,7 @@ private:
Filename _filename;
BamEndian _file_endian;
BamTextureMode _file_texture_mode;
// This is the set of all TypeHandles already written.
pset<int, int_hash> _types_written;

View File

@ -53,6 +53,11 @@ ConfigVariableEnum<BamEndian> bam_endian
"may set it to \"littleendian\" or \"bigendian\" to target a "
"particular platform."));
ConfigVariableEnum<BamTextureMode> bam_texture_mode
("bam-texture-mode", BTM_relative,
PRC_DESC("Set this to specify how textures should be written into Bam files."
"See the panda source or documentation for available options."));
ConfigVariableSearchPath model_path
("model-path",
PRC_DESC("The default directories to search for all models and general "

View File

@ -24,6 +24,7 @@
#include "configVariableSearchPath.h"
#include "configVariableEnum.h"
#include "bamEndian.h"
#include "bamTextureMode.h"
#include "dconfig.h"
class DSearchPath;
@ -36,9 +37,10 @@ NotifyCategoryDecl(bam, EXPCL_PANDA, EXPTP_PANDA);
// because we must be able to access it at static init time. Instead
// of declaring it a global constant, we'll make it a member of
// MemoryUsage.
//extern EXPCL_PANDA const bool track_memory_usage;
extern EXPCL_PANDA ConfigVariableEnum<BamEndian> bam_endian;
extern EXPCL_PANDA ConfigVariableEnum<BamTextureMode> bam_texture_mode;
extern EXPCL_PANDA ConfigVariableSearchPath model_path;
extern EXPCL_PANDA ConfigVariableSearchPath texture_path;

View File

@ -16,8 +16,8 @@
//
////////////////////////////////////////////////////////////////////
#ifndef LOAD_EGG_FILE_H
#define LOAD_EGG_FILE_H
#ifndef LOAD_PRC_FILE_H
#define LOAD_PRC_FILE_H
#include "pandabase.h"

View File

@ -2,6 +2,7 @@
#include "bamEndian.cxx"
#include "bamReader.cxx"
#include "bamReaderParam.cxx"
#include "bamTextureMode.cxx"
#include "bamWriter.cxx"
#include "bitArray.cxx"
#include "bitMask.cxx"

View File

@ -33,6 +33,10 @@ DynamicTextPage::
DynamicTextPage(DynamicTextFont *font, int page_number) :
_font(font)
{
// Since the texture might change frequently, don't try to compress
// it by default.
_compression = CM_off;
_x_size = _font->get_page_x_size();
_y_size = _font->get_page_y_size();

View File

@ -21,6 +21,7 @@
#include "bamFile.h"
#include "pandaNode.h"
#include "geomNode.h"
#include "texture.h"
#include "recorderHeader.h"
#include "recorderFrame.h"
#include "recorderTable.h"
@ -145,6 +146,10 @@ get_info(const Filename &filename) {
objects[0]->is_of_type(PandaNode::get_class_type())) {
describe_scene_graph(DCAST(PandaNode, objects[0]));
} else if (objects.size() == 1 &&
objects[0]->is_of_type(Texture::get_class_type())) {
describe_texture(DCAST(Texture, objects[0]));
} else if (!objects.empty() && objects[0]->is_of_type(RecorderHeader::get_class_type())) {
describe_session(DCAST(RecorderHeader, objects[0]), objects);
@ -189,6 +194,16 @@ describe_scene_graph(PandaNode *node) {
}
}
////////////////////////////////////////////////////////////////////
// Function: BamInfo::describe_texture
// Access: Private
// Description: Called for Bam files that contain a Texture object.
////////////////////////////////////////////////////////////////////
void BamInfo::
describe_texture(Texture *tex) {
tex->write(nout, 2);
}
////////////////////////////////////////////////////////////////////
// Function: BamInfo::describe_session
// Access: Private

View File

@ -48,6 +48,7 @@ private:
bool get_info(const Filename &filename);
void describe_scene_graph(PandaNode *node);
void describe_texture(Texture *tex);
void describe_session(RecorderHeader *header, const Objects &objects);
void describe_general_object(TypedWritable *object);
void list_hierarchy(PandaNode *node, int indent_level);

View File

@ -24,6 +24,18 @@
#include "config_egg2pg.h"
#include "config_gobj.h"
#include "config_chan.h"
#include "pandaNode.h"
#include "geomNode.h"
#include "renderState.h"
#include "textureAttrib.h"
#include "dcast.h"
#include "graphicsPipeSelection.h"
#include "graphicsEngine.h"
#include "graphicsBuffer.h"
#include "graphicsStateGuardian.h"
#include "load_prc_file.h"
#include "windowProperties.h"
#include "frameBufferProperties.h"
////////////////////////////////////////////////////////////////////
// Function: EggToBam::Constructor
@ -36,7 +48,10 @@ EggToBam() :
{
set_program_description
("This program reads Egg files and outputs Bam files, the binary format "
"suitable for direct loading of animation and models into Panda.");
"suitable for direct loading of animation and models into Panda. Bam "
"files are tied to a particular version of Panda, so should not be "
"considered replacements for egg files, but they tend to be smaller and "
"load much faster than the equivalent egg files.");
// -f is always in effect for egg2bam. It doesn't make sense to
// provide it as an option to the user.
@ -107,6 +122,53 @@ EggToBam() :
"way to ensure the bam file is completely self-contained.",
&EggToBam::dispatch_none, &_tex_rawdata);
add_option
("txo", "", 0,
"Rather than writing texture data directly into the bam file, as in "
"-rawtex, create a texture object for each referenced texture. A "
"texture object is a kind of mini-bam file, with a .txo extension, "
"that contains all of the data needed to recreate a texture, including "
"its image contents, filter and wrap settings, and so on. 3-D textures "
"and cube maps can also be represented in a single .txo file. Texture "
"object files, like bam files, are tied to a particular version of "
"Panda.",
&EggToBam::dispatch_none, &_tex_txo);
#ifdef HAVE_ZLIB
add_option
("txopz", "", 0,
"In addition to writing texture object files as above, compress each "
"one using pzip to a .txo.pz file. In many cases, this will yield a "
"disk file size comparable to that achieved by png compression. This "
"is an on-disk compression only, and does not affect the amount of "
"RAM or texture memory consumed by the texture when it is loaded.",
&EggToBam::dispatch_none, &_tex_txopz);
#endif // HAVE_ZLIB
add_option
("ctex", "", 0,
"Asks the graphics card to pre-compress the texture images when using "
"-rawtex or -txo. "
#ifdef HAVE_ZLIB
"This is unrelated to the on-disk compression achieved "
"via -txopz (and it may be used in conjunction with that parameter). "
#endif // HAVE_ZLIB
"This will result in a smaller RAM and texture memory footprint for "
"the texture images. The same "
"effect can be achieved at load time by setting compressed-textures in "
"your Config.prc file; but -ctex pre-compresses the "
"textures so that they do not need to be compressed at load time. "
"Note that using -ctex makes .txo files that are only guaranteed to load "
"on the particular graphics card that was used to generate them.",
&EggToBam::dispatch_none, &_tex_ctex);
add_option
("load-display", "display name", 0,
"Specifies the particular display module to load to perform the texture "
"compression requested by -ctex. If this is omitted, the default is "
"taken from the Config.prc file.",
&EggToBam::dispatch_string, NULL, &_load_display);
redescribe_option
("cs",
"Specify the coordinate system of the resulting " + _format_name +
@ -118,6 +180,7 @@ EggToBam() :
_egg_flatten = 0;
_egg_combine_geoms = 0;
_egg_suppress_hidden = 1;
_tex_txopz = false;
}
////////////////////////////////////////////////////////////////////
@ -162,6 +225,31 @@ run() {
nout << "Unable to build scene graph from egg file.\n";
exit(1);
}
if (_tex_ctex) {
if (!make_buffer()) {
nout << "Unable to initialize graphics context; cannot compress textures.\n";
exit(1);
}
}
if (_tex_txo || _tex_txopz || (_tex_ctex && _tex_rawdata)) {
collect_textures(root);
Textures::iterator ti;
for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
Texture *tex = (*ti);
if (_tex_ctex) {
tex->set_keep_ram_image(true);
tex->set_compression(Texture::CM_on);
if (!_engine->extract_texture_data(tex, _gsg)) {
nout << " couldn't compress " << tex->get_name() << "\n";
}
}
if (_tex_txo || _tex_txopz) {
convert_txo(tex);
}
}
}
if (_ls) {
root->ls(nout, 0);
@ -170,7 +258,7 @@ run() {
// This should be guaranteed because we pass false to the
// constructor, above.
nassertv(has_output_filename());
Filename filename = get_output_filename();
filename.make_dir();
nout << "Writing " << filename << "\n";
@ -214,6 +302,158 @@ handle_args(ProgramBase::Args &args) {
return EggToSomething::handle_args(args);
}
////////////////////////////////////////////////////////////////////
// Function: EggToBam::collect_textures
// Access: Private
// Description: Recursively walks the scene graph, looking for
// Texture references.
////////////////////////////////////////////////////////////////////
void EggToBam::
collect_textures(PandaNode *node) {
collect_textures(node->get_state());
if (node->is_geom_node()) {
GeomNode *geom_node = DCAST(GeomNode, node);
int num_geoms = geom_node->get_num_geoms();
for (int i = 0; i < num_geoms; ++i) {
collect_textures(geom_node->get_geom_state(i));
}
}
PandaNode::Children children = node->get_children();
int num_children = children.get_num_children();
for (int i = 0; i < num_children; ++i) {
collect_textures(children.get_child(i));
}
}
////////////////////////////////////////////////////////////////////
// Function: EggToBam::collect_textures
// Access: Private
// Description: Recursively walks the scene graph, looking for
// Texture references.
////////////////////////////////////////////////////////////////////
void EggToBam::
collect_textures(const RenderState *state) {
const TextureAttrib *tex_attrib = state->get_texture();
if (tex_attrib != (TextureAttrib *)NULL) {
int num_on_stages = tex_attrib->get_num_on_stages();
for (int i = 0; i < num_on_stages; ++i) {
_textures.insert(tex_attrib->get_on_texture(tex_attrib->get_on_stage(i)));
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggToBam::convert_txo
// Access: Private
// Description: If the indicated Texture was not already loaded from
// a txo file, writes it to a txo file and updates the
// Texture object to reference the new file.
////////////////////////////////////////////////////////////////////
void EggToBam::
convert_txo(Texture *tex) {
if (!tex->get_loaded_from_txo()) {
Filename fullpath = tex->get_fullpath().get_filename_index(0);
if (_tex_txopz) {
fullpath.set_extension("txo.pz");
// We use this clumsy syntax so that the new extension appears to be
// two separate extensions, .txo followed by .pz, which is what
// Texture::write() expects to find.
fullpath = Filename(fullpath.get_fullpath());
} else {
fullpath.set_extension("txo");
}
if (tex->write(fullpath)) {
nout << " Writing " << fullpath;
if (tex->get_ram_image_compression() != Texture::CM_off) {
nout << " (compressed " << tex->get_ram_image_compression() << ")";
}
nout << "\n";
tex->set_loaded_from_txo();
tex->set_fullpath(fullpath);
tex->clear_alpha_fullpath();
Filename filename = tex->get_filename().get_filename_index(0);
if (_tex_txopz) {
filename.set_extension("txo.pz");
filename = Filename(filename.get_fullpath());
} else {
filename.set_extension("txo");
}
tex->set_filename(filename);
tex->clear_alpha_filename();
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggToBam::make_buffer
// Access: Private
// Description: Creates a GraphicsBuffer for communicating with the
// graphics card.
////////////////////////////////////////////////////////////////////
bool EggToBam::
make_buffer() {
if (!_load_display.empty()) {
// Override the user's config file with the command-line parameter.
string prc = "load-display " + _load_display;
load_prc_file_data("prc", prc);
}
GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
_pipe = selection->make_default_pipe();
if (_pipe == (GraphicsPipe *)NULL) {
nout << "Unable to create graphics pipe.\n";
return false;
}
_engine = new GraphicsEngine;
FrameBufferProperties fbprops = FrameBufferProperties::get_default();
// Some graphics drivers can only create single-buffered offscreen
// buffers. So request that.
fbprops.set_frame_buffer_mode((fbprops.get_frame_buffer_mode() & ~FrameBufferProperties::FM_buffer) | FrameBufferProperties::FM_single_buffer);
PT(GraphicsStateGuardian) gsg =
_engine->make_gsg(_pipe, fbprops);
if (gsg == (GraphicsStateGuardian *)NULL) {
nout << "Unable to create GraphicsStateGuardian.\n";
return false;
}
_gsg = gsg;
// We don't care how big the buffer is; we just need it to manifest
// the GSG.
_buffer = _engine->make_buffer(_gsg, "buffer", 0, 1, 1);
_engine->open_windows();
if (_buffer == (GraphicsOutput *)NULL || !_buffer->is_valid()) {
if (_buffer != (GraphicsOutput *)NULL) {
_engine->remove_window(_buffer);
}
// Couldn't create a buffer; try for a window instead.
GraphicsWindow *window = _engine->make_window(_gsg, "window", 0);
if (window == (GraphicsWindow *)NULL) {
nout << "Unable to create graphics window.\n";
return false;
}
WindowProperties props;
props.set_size(1, 1);
props.set_origin(0, 0);
props.set_undecorated(true);
props.set_open(true);
props.set_z_order(WindowProperties::Z_bottom);
window->request_properties(props);
_buffer = window;
_engine->open_windows();
}
return true;
}
int main(int argc, char *argv[]) {
EggToBam prog;

View File

@ -22,6 +22,15 @@
#include "pandatoolbase.h"
#include "eggToSomething.h"
#include "pset.h"
#include "graphicsPipe.h"
class PandaNode;
class RenderState;
class Texture;
class GraphicsEngine;
class GraphicsStateGuardian;
class GraphicsOutput;
////////////////////////////////////////////////////////////////////
// Class : EggToBam
@ -37,6 +46,16 @@ protected:
virtual bool handle_args(Args &args);
private:
void collect_textures(PandaNode *node);
void collect_textures(const RenderState *state);
void convert_txo(Texture *tex);
bool make_buffer();
private:
typedef pset<Texture *> Textures;
Textures _textures;
bool _has_egg_flatten;
int _egg_flatten;
bool _has_egg_combine_geoms;
@ -47,6 +66,16 @@ private:
int _compression_quality;
bool _compression_off;
bool _tex_rawdata;
bool _tex_txo;
bool _tex_txopz;
bool _tex_ctex;
string _load_display;
// The rest of this is required to support -ctex.
PT(GraphicsPipe) _pipe;
GraphicsStateGuardian *_gsg;
GraphicsEngine *_engine;
GraphicsOutput *_buffer;
};
#endif