explicit mipmap control

This commit is contained in:
David Rose 2006-03-22 22:23:56 +00:00
parent 020a09fc9c
commit f803ef85ce
26 changed files with 5718 additions and 4400 deletions

View File

@ -192,7 +192,8 @@ class Loader:
return font
# texture loading funcs
def loadTexture(self, texturePath, alphaPath = None):
def loadTexture(self, texturePath, alphaPath = None,
readMipmaps = False):
"""
texturePath is a string.
@ -203,15 +204,15 @@ class Loader:
assert Loader.notify.debug("Loading texture: %s" % (texturePath))
if phaseChecker:
phaseChecker(texturePath)
texture = TexturePool.loadTexture(texturePath)
texture = TexturePool.loadTexture(texturePath, 0, readMipmaps)
else:
assert Loader.notify.debug("Loading texture: %s %s" % (texturePath, alphaPath))
if phaseChecker:
phaseChecker(texturePath)
texture = TexturePool.loadTexture(texturePath, alphaPath)
texture = TexturePool.loadTexture(texturePath, alphaPath, 0, 0, readMipmaps)
return texture
def load3DTexture(self, texturePattern):
def load3DTexture(self, texturePattern, readMipmaps = False):
"""
texturePattern is a string that contains a sequence of one or
more '#' characters, which will be filled in with the sequence
@ -223,10 +224,10 @@ class Loader:
assert Loader.notify.debug("Loading 3-D texture: %s" % (texturePattern))
if phaseChecker:
phaseChecker(texturePattern)
texture = TexturePool.load3dTexture(texturePattern)
texture = TexturePool.load3dTexture(texturePattern, readMipmaps)
return texture
def loadCubeMap(self, texturePattern):
def loadCubeMap(self, texturePattern, readMipmaps = False):
"""
texturePattern is a string that contains a sequence of one or
more '#' characters, which will be filled in with the sequence
@ -239,7 +240,7 @@ class Loader:
assert Loader.notify.debug("Loading cube map: %s" % (texturePattern))
if phaseChecker:
phaseChecker(texturePattern)
texture = TexturePool.loadCubeMap(texturePattern)
texture = TexturePool.loadCubeMap(texturePattern, readMipmaps)
return texture
def unloadTexture(self, texture):

View File

@ -139,6 +139,16 @@ appear before they are referenced.
alpha channels along with image file formats like JPEG that don't
traditionally support alpha channels.
<Scalar> alpha-file-channel { channel }
This defines the channel that should be extracted from the file
named by alpha-file to determine the alpha channel for the
resulting channel. The default is 0, which means the grayscale
combination of r, g, b. Otherwise, this should be the 1-based
channel number, for instance 1, 2, or 3 for r, g, or b,
respectively, or 4 for the alpha channel of a four-component
image.
<Scalar> format { format-definition }
This defines the load format of the image file. The
@ -223,6 +233,22 @@ appear before they are referenced.
present, must be included in the same image with the color
channel(s).
<Scalar> read-mipmaps { flag }
If this flag is nonzero, then pre-generated mipmap levels will be
loaded along with the texture. In this case, the filename should
contain a sequence of one or more hash mark ("#") characters,
which will be filled in with the mipmap level number; the texture
filename thus determines a series of images, one for each mipmap
level. The base texture image is mipmap level 0.
If this flag is specified in conjunction with a 3D or cube map
texture (as specified above), then the filename should contain two
hash mark sequences, separated by a character such as an
underscore, hyphen, or dot. The first sequence will be filled in
with the mipmap level index, and the second sequence will be
filled in with the 3D sequence or cube map face.
<Scalar> minfilter { filter-type }
<Scalar> magfilter { filter-type }
<Scalar> magfilteralpha { filter-type }
@ -327,6 +353,12 @@ appear before they are referenced.
<Scalar> combine-alpha-source2 { constant }
<Scalar> combine-alpha-operand2 { src-alpha }
<Scalar> saved-result { flag }
If flag is nonzero, then it indicates that this particular texture
stage will be supplied as the "last_saved_result" source for any
future texture stages.
<Scalar> tex-gen { mode }
This specifies that texture coordinates for the primitives that

View File

@ -929,6 +929,39 @@ get_alpha_file_channel() const {
return _alpha_file_channel;
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::set_read_mipmaps
// Access: Published
// Description: Sets the read_mipmaps flag.
//
// If read_mipmaps is true, the filename should contain
// a hash mark ('#'), which will be filled in with the
// mipmap level number; and the texture will be defined
// with a series of images, one for each mipmap level.
//
// If the filename is of a time that already requires a
// hash mark, such as a cube map or a 3-d texture, then
// the filename should now require two hash marks, and
// the first one indicates the mipmap level number,
// while the second indicates the face number or 3-d
// level number.
////////////////////////////////////////////////////////////////////
INLINE void EggTexture::
set_read_mipmaps(bool read_mipmaps) {
_read_mipmaps = read_mipmaps;
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::get_read_mipmaps
// Access: Published
// Description: Returns the current setting of the read_mipmaps flag.
// See set_read_mipmaps().
////////////////////////////////////////////////////////////////////
INLINE bool EggTexture::
get_read_mipmaps() const {
return _read_mipmaps;
}
////////////////////////////////////////////////////////////////////
// Function: EggTexture::get_multitexture_sort
// Access: Published

View File

@ -53,6 +53,7 @@ EggTexture(const string &tref_name, const string &filename)
_border_color.set(0.0f, 0.0f, 0.0f, 1.0f);
_flags = 0;
_alpha_file_channel = 0;
_read_mipmaps = false;
_multitexture_sort = 0;
}
@ -103,6 +104,7 @@ operator = (const EggTexture &copy) {
_alpha_filename = copy._alpha_filename;
_alpha_fullpath = copy._alpha_fullpath;
_alpha_file_channel = copy._alpha_file_channel;
_read_mipmaps = copy._read_mipmaps;
_multitexture_sort = 0;
_combiner[0] = copy._combiner[0];
_combiner[1] = copy._combiner[1];
@ -144,6 +146,11 @@ write(ostream &out, int indent_level) const {
<< get_alpha_file_channel() << " }\n";
}
if (get_read_mipmaps()) {
indent(out, indent_level + 2)
<< "<Scalar> read-mipmaps { 1 }\n";
}
if (get_texture_type() != TT_unspecified) {
indent(out, indent_level + 2)
<< "<Scalar> texture_type { " << get_texture_type() << " }\n";

View File

@ -254,6 +254,10 @@ PUBLISHED:
INLINE bool has_alpha_file_channel() const;
INLINE int get_alpha_file_channel() const;
INLINE void set_read_mipmaps(bool read_mipmaps);
INLINE bool get_read_mipmaps() const;
void clear_multitexture();
bool multitexture_over(EggTexture *other);
INLINE int get_multitexture_sort() const;
@ -312,6 +316,7 @@ private:
Filename _alpha_filename;
Filename _alpha_fullpath;
int _alpha_file_channel;
bool _read_mipmaps;
int _multitexture_sort;
class SourceAndOperand {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -654,6 +654,9 @@ texture_body:
} else if (cmp_nocase_uh(name, "alpha_file_channel") == 0) {
texture->set_alpha_file_channel((int)value);
} else if (cmp_nocase_uh(name, "read_mipmaps") == 0) {
texture->set_read_mipmaps(((int)value) != 0);
} else {
eggyywarning("Unsupported texture scalar: " + name);
}

View File

@ -886,19 +886,23 @@ load_texture(TextureDef &def, const EggTexture *egg_tex) {
tex = TexturePool::load_texture(egg_tex->get_fullpath(),
egg_tex->get_alpha_fullpath(),
wanted_channels,
egg_tex->get_alpha_file_channel());
egg_tex->get_alpha_file_channel(),
egg_tex->get_read_mipmaps());
} else {
tex = TexturePool::load_texture(egg_tex->get_fullpath(),
wanted_channels);
wanted_channels,
egg_tex->get_read_mipmaps());
}
break;
case EggTexture::TT_3d_texture:
tex = TexturePool::load_3d_texture(egg_tex->get_fullpath());
tex = TexturePool::load_3d_texture(egg_tex->get_fullpath(),
egg_tex->get_read_mipmaps());
break;
case EggTexture::TT_cube_map:
tex = TexturePool::load_cube_map(egg_tex->get_fullpath());
tex = TexturePool::load_cube_map(egg_tex->get_fullpath(),
egg_tex->get_read_mipmaps());
break;
}

View File

@ -470,27 +470,47 @@ reset() {
if (is_at_least_version(1, 3)) {
_supports_compressed_texture = true;
_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexImage1D");
_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexImage2D");
_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexImage3D");
_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexSubImage1D");
_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexSubImage2D");
_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexSubImage3D");
_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)
get_extension_func(GLPREFIX_QUOTED, "GetCompressedTexImage");
} else if (has_extension("GL_ARB_texture_compression")) {
_supports_compressed_texture = true;
_glCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexImage1DARB");
_glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexImage2DARB");
_glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexImage3DARB");
_glCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexSubImage1DARB");
_glCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexSubImage2DARB");
_glCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)
get_extension_func(GLPREFIX_QUOTED, "CompressedTexSubImage3DARB");
_glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)
get_extension_func(GLPREFIX_QUOTED, "GetCompressedTexImageARB");
}
if (_supports_compressed_texture) {
if (_glCompressedTexImage2D == NULL ||
if (_glCompressedTexImage1D == NULL ||
_glCompressedTexImage2D == NULL ||
_glCompressedTexImage3D == NULL ||
_glCompressedTexSubImage1D == NULL ||
_glCompressedTexSubImage2D == NULL ||
_glCompressedTexSubImage3D == NULL ||
_glGetCompressedTexImage == NULL) {
GLCAT.warning()
<< "Compressed textures advertised as supported by OpenGL runtime, but could not get pointers to extension functions.\n";
@ -2394,77 +2414,28 @@ extract_texture_data(Texture *tex) {
PTA_uchar image;
size_t page_size = 0;
if (target == GL_TEXTURE_CUBE_MAP) {
// A cube map, compressed or uncompressed. This we must extract
// one page at a time.
// If the cube map is compressed, we assume that all the
// compressed pages are exactly the same size. OpenGL doesn't
// make this assumption, but it happens to be true for all
// currently extant compression schemes, and it makes things
// simpler for us. (It also makes things much simpler for the
// graphics hardware, so it's likely to continue to be true for a
// while at least.)
GLenum external_format = get_external_image_format(tex);
GLenum pixel_type = get_component_type(type);
page_size = tex->get_expected_ram_page_size();
if (compression != Texture::CM_off) {
GLint image_size;
GLP(GetTexLevelParameteriv)(page_target, 0,
GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size);
nassertr(image_size <= (int)page_size, false);
page_size = image_size;
}
image = PTA_uchar::empty_array(page_size * 6);
for (int z = 0; z < 6; ++z) {
page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z;
if (compression == Texture::CM_off) {
GLP(GetTexImage)(page_target, 0, external_format, pixel_type,
image.p() + z * page_size);
} else {
_glGetCompressedTexImage(page_target, 0, image.p() + z * page_size);
}
}
} else if (compression == Texture::CM_off) {
// An uncompressed 1-d, 2-d, or 3-d texture.
image = PTA_uchar::empty_array(tex->get_expected_ram_image_size());
GLenum external_format = get_external_image_format(tex);
GLenum pixel_type = get_component_type(type);
GLP(GetTexImage)(target, 0, external_format, pixel_type, image.p());
} else {
// A compressed 1-d, 2-d, or 3-d texture.
GLint image_size;
GLP(GetTexLevelParameteriv)(target, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size);
page_size = image_size / tex->get_z_size();
image = PTA_uchar::empty_array(image_size);
_glGetCompressedTexImage(target, 0, image.p());
}
// Now see if we were successful.
error_code = GLP(GetError)();
if (error_code != GL_NO_ERROR) {
const GLubyte *error_string = GLUP(ErrorString)(error_code);
GLCAT.error()
<< "Unable to extract texture for " << tex->get_name();
if (error_string != (const GLubyte *)NULL) {
GLCAT.error(false)
<< " : " << error_string;
}
GLCAT.error(false)
<< "\n";
if (!extract_texture_image(image, page_size, tex, target, page_target,
type, compression, 0)) {
return false;
}
tex->set_ram_image(image, compression, page_size);
if (tex->uses_mipmaps()) {
// Also get the mipmap levels.
GLint num_expected_levels = tex->get_expected_num_mipmap_levels();
GLint highest_level = num_expected_levels;
GLP(GetTexParameteriv)(target, GL_TEXTURE_MAX_LEVEL, &highest_level);
highest_level = min(highest_level, num_expected_levels);
for (int n = 1; n <= highest_level; ++n) {
if (!extract_texture_image(image, page_size, tex, target, page_target,
type, compression, n)) {
return false;
}
tex->set_ram_mipmap_image(n, image, page_size);
}
}
return true;
}
@ -5619,8 +5590,7 @@ do_issue_texture() {
// Description:
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
update_standard_texture_bindings()
{
update_standard_texture_bindings() {
int num_stages = _target._texture->get_num_on_stages();
int num_old_stages = _max_texture_stages;
if (_state._texture != (TextureAttrib *)NULL) {
@ -6147,7 +6117,6 @@ specify_texture(Texture *tex) {
Texture::FilterType minfilter = tex->get_minfilter();
Texture::FilterType magfilter = tex->get_magfilter();
bool uses_mipmaps = tex->uses_mipmaps() && !CLP(ignore_mipmaps);
Texture::CompressionMode image_compression = tex->get_ram_image_compression();
#ifndef NDEBUG
if (CLP(force_mipmaps)) {
@ -6157,20 +6126,17 @@ specify_texture(Texture *tex) {
}
#endif
if (_supports_generate_mipmap &&
(auto_generate_mipmaps || !tex->might_have_ram_image() ||
image_compression != Texture::CM_off)) {
// If the hardware can automatically generate mipmaps, ask it to
// do so now, but only if the texture requires them.
GLP(TexParameteri)(target, GL_GENERATE_MIPMAP, uses_mipmaps);
if (!tex->might_have_ram_image()) {
// If it's a dynamically generated texture (that is, the RAM image
// isn't available so it didn't pass through the CPU), we should
// enable GL-generated mipmaps here if we can.
if (_supports_generate_mipmap) {
GLP(TexParameteri)(target, GL_GENERATE_MIPMAP, uses_mipmaps);
} else if (!tex->might_have_ram_image()) {
// If the hardware can't automatically generate mipmaps, but it's
// a dynamically generated texture (that is, the RAM image isn't
// available so it didn't pass through the CPU), then we'd better
// not try to enable mipmap filtering, since we can't generate
// mipmaps.
uses_mipmaps = false;
} else {
// Otherwise, don't try to use mipmaps.
uses_mipmaps = false;
}
}
GLP(TexParameteri)(target, GL_TEXTURE_MIN_FILTER,
@ -6417,53 +6383,42 @@ upload_texture(CLP(TextureContext) *gtc) {
return false;
}
size_t page_size = tex->get_ram_page_size();
const unsigned char *image_base = image;
success = success && upload_texture_image
(gtc, uses_mipmaps, GL_TEXTURE_CUBE_MAP_POSITIVE_X,
internal_format, width, height, depth, external_format, component_type,
image_base, page_size, image_compression);
image_base += page_size;
true, 0, image_compression);
success = success && upload_texture_image
(gtc, uses_mipmaps, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
internal_format, width, height, depth, external_format, component_type,
image_base, page_size, image_compression);
image_base += page_size;
true, 1, image_compression);
success = success && upload_texture_image
(gtc, uses_mipmaps, GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
internal_format, width, height, depth, external_format, component_type,
image_base, page_size, image_compression);
image_base += page_size;
true, 2, image_compression);
success = success && upload_texture_image
(gtc, uses_mipmaps, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
internal_format, width, height, depth, external_format, component_type,
image_base, page_size, image_compression);
image_base += page_size;
true, 3, image_compression);
success = success && upload_texture_image
(gtc, uses_mipmaps, GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
internal_format, width, height, depth, external_format, component_type,
image_base, page_size, image_compression);
image_base += page_size;
true, 4, image_compression);
success = success && upload_texture_image
(gtc, uses_mipmaps, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
internal_format, width, height, depth, external_format, component_type,
image_base, page_size, image_compression);
image_base += page_size;
nassertr((size_t)(image_base - image) == image.size(), false);
true, 5, image_compression);
} else {
// Any other kind of texture can be loaded all at once.
success = upload_texture_image
(gtc, uses_mipmaps, get_texture_target(tex->get_texture_type()),
internal_format, width, height, depth, external_format, component_type,
image, image.size(), image_compression);
false, 0, image_compression);
}
if (success) {
@ -6477,12 +6432,6 @@ upload_texture(CLP(TextureContext) *gtc) {
gtc->update_data_size_bytes(get_texture_memory_size(tex));
#endif
#ifndef NDEBUG
if (uses_mipmaps && CLP(save_mipmaps)) {
save_mipmap_images(tex);
}
#endif
report_my_gl_errors();
return true;
}
@ -6503,8 +6452,7 @@ upload_texture_image(CLP(TextureContext) *gtc,
GLenum target, GLint internal_format,
int width, int height, int depth,
GLint external_format, GLenum component_type,
const unsigned char *image,
size_t image_size,
bool one_page_only, int z,
Texture::CompressionMode image_compression) {
// Make sure the error stack is cleared out before we begin.
report_my_gl_errors();
@ -6518,152 +6466,215 @@ upload_texture_image(CLP(TextureContext) *gtc,
}
PStatTimer timer(_load_texture_pcollector);
if (uses_mipmaps) {
#ifdef DO_PSTATS
_data_transferred_pcollector.add_level(image_size * 4 / 3);
#endif
#ifndef NDEBUG
if (CLP(show_mipmaps) && target == GL_TEXTURE_2D) {
build_phony_mipmaps(gtc->get_texture());
report_my_gl_errors();
return true;
} else
#endif
if (!_supports_generate_mipmap ||
(!auto_generate_mipmaps && image_compression == Texture::CM_off)) {
// We only need to build the mipmaps by hand if (a) the GL
// doesn't support generating them automatically, or (b) the
// user has specifically requested we don't use GL's
// auto-mipmap feature (but if the source texture image is
// compressed, we can't build mipmaps, so we ignore
// auto_generate_mipmaps in that case).
bool success = true;
switch (target) {
case GL_TEXTURE_1D:
GLUP(Build1DMipmaps)(target, internal_format, width,
external_format, component_type, image);
break;
case GL_TEXTURE_3D:
#ifdef GLU_VERSION_1_3
GLUP(Build3DMipmaps)(target, internal_format,
width, height, depth,
external_format, component_type, image);
#else // GLU_VERSION_1_3
// Prior to GLU 1.3, there was no gluBuild3DMipmaps() call.
// Just fall through and load the texture without mipmaps.
GLP(TexParameteri)(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
success = false;
#endif // GLU_VERSION_1_3
break;
default:
GLUP(Build2DMipmaps)(target, internal_format,
width, height,
external_format, component_type, image);
}
report_my_gl_errors();
if (success) {
return true;
}
}
}
Texture *tex = gtc->get_texture();
if (GLCAT.is_debug()) {
if (image_compression != Texture::CM_off) {
GLCAT.debug()
<< "loading pre-compressed texture " << gtc->get_texture()->get_name() << "\n";
<< "loading pre-compressed texture " << tex->get_name() << "\n";
} else if (is_compressed_format(internal_format)) {
GLCAT.debug()
<< "compressing texture " << gtc->get_texture()->get_name() << "\n";
<< "compressing texture " << tex->get_name() << "\n";
}
}
int num_ram_mipmap_levels = 1;
bool load_ram_mipmaps = false;
if (uses_mipmaps) {
num_ram_mipmap_levels = tex->get_num_ram_mipmap_images();
if (num_ram_mipmap_levels == 1) {
// No RAM mipmap levels available. Should we generate some?
if (!_supports_generate_mipmap ||
(!auto_generate_mipmaps && image_compression == Texture::CM_off)) {
// Yes, the GL won't generate them, so we need to.
tex->generate_ram_mipmap_images();
num_ram_mipmap_levels = tex->get_num_ram_mipmap_images();
}
}
if (num_ram_mipmap_levels != 1) {
// We will load the mipmap levels from RAM. Don't ask the GL to
// generate them.
if (_supports_generate_mipmap) {
GLP(TexParameteri)(target, GL_GENERATE_MIPMAP, false);
}
load_ram_mipmaps = true;
} else {
// We don't have mipmap levels in RAM. Ask the GL to generate
// them if it can.
if (_supports_generate_mipmap) {
GLP(TexParameteri)(target, GL_GENERATE_MIPMAP, true);
} else {
// If it can't, do without mipmaps.
GLP(TexParameteri)(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
uses_mipmaps = false;
}
}
}
int highest_level = 0;
if (!gtc->_already_applied ||
gtc->_internal_format != internal_format ||
gtc->_width != width ||
gtc->_height != height ||
gtc->_depth != depth) {
// We need to reload a new image.
for (int n = 0; n < num_ram_mipmap_levels; ++n) {
const unsigned char *image = tex->get_ram_mipmap_image(n);
if (image == (const unsigned char *)NULL) {
GLCAT.warning()
<< "No mipmap level " << n << " defined for " << tex->get_name()
<< "\n";
// No mipmap level n; stop here.
break;
}
size_t image_size = tex->get_ram_mipmap_image_size(n);
if (one_page_only) {
image_size = tex->get_ram_mipmap_page_size(n);
image += image_size * z;
}
#ifdef DO_PSTATS
_data_transferred_pcollector.add_level(image_size);
_data_transferred_pcollector.add_level(image_size);
#endif
switch (target) {
case GL_TEXTURE_1D:
nassertr(image_compression == Texture::CM_off, false);
GLP(TexImage1D)(target, 0, internal_format,
width, 0,
external_format, component_type, image);
break;
case GL_TEXTURE_3D:
if (_supports_3d_texture) {
nassertr(image_compression == Texture::CM_off, false);
_glTexImage3D(target, 0, internal_format,
width, height, depth, 0,
external_format, component_type, image);
} else {
report_my_gl_errors();
return false;
switch (target) {
case GL_TEXTURE_1D:
if (image_compression == Texture::CM_off) {
GLP(TexImage1D)(target, n, internal_format,
width, 0,
external_format, component_type, image);
} else {
_glCompressedTexImage1D(target, n, external_format, width,
0, image_size, image);
}
break;
case GL_TEXTURE_3D:
if (_supports_3d_texture) {
if (image_compression == Texture::CM_off) {
_glTexImage3D(target, n, internal_format,
width, height, depth, 0,
external_format, component_type, image);
} else {
_glCompressedTexImage3D(target, n, external_format, width,
height, depth,
0, image_size, image);
}
} else {
report_my_gl_errors();
return false;
}
break;
default:
if (image_compression == Texture::CM_off) {
GLP(TexImage2D)(target, n, internal_format,
width, height, 0,
external_format, component_type, image);
} else {
_glCompressedTexImage2D(target, n, external_format, width, height,
0, image_size, image);
}
}
break;
default:
if (image_compression == Texture::CM_off) {
GLP(TexImage2D)(target, 0, internal_format,
width, height, 0,
external_format, component_type, image);
} else {
_glCompressedTexImage2D(target, 0, external_format, width, height,
0, image_size, image);
}
highest_level = n;
width = max(width >> 1, 1);
height = max(height >> 1, 1);
depth = max(depth >> 1, 1);
}
} else {
// We can reload the image over the previous image, possibly
// saving on texture memory fragmentation.
for (int n = 0; n < num_ram_mipmap_levels; ++n) {
const unsigned char *image = tex->get_ram_mipmap_image(n);
if (image == (const unsigned char *)NULL) {
GLCAT.warning()
<< "No mipmap level " << n << " defined for " << tex->get_name()
<< "\n";
// No mipmap level n; stop here.
break;
}
size_t image_size = tex->get_ram_mipmap_image_size(n);
if (one_page_only) {
image_size = tex->get_ram_mipmap_page_size(n);
image += image_size * z;
}
#ifdef DO_PSTATS
_data_transferred_pcollector.add_level(image_size);
_data_transferred_pcollector.add_level(image_size);
#endif
switch (target) {
case GL_TEXTURE_1D:
nassertr(image_compression == Texture::CM_off, false);
GLP(TexSubImage1D)(target, 0, 0, width,
external_format, component_type, image);
break;
switch (target) {
case GL_TEXTURE_1D:
if (image_compression == Texture::CM_off) {
GLP(TexSubImage1D)(target, n, 0, width,
external_format, component_type, image);
} else {
_glCompressedTexSubImage1D(target, n, 0, width,
external_format, image_size, image);
}
break;
case GL_TEXTURE_3D:
if (_supports_3d_texture) {
nassertr(image_compression == Texture::CM_off, false);
_glTexSubImage3D(target, 0, 0, 0, 0, width, height, depth,
external_format, component_type, image);
} else {
report_my_gl_errors();
return false;
case GL_TEXTURE_3D:
if (_supports_3d_texture) {
if (image_compression == Texture::CM_off) {
_glTexSubImage3D(target, n, 0, 0, 0, width, height, depth,
external_format, component_type, image);
} else {
_glCompressedTexSubImage3D(target, n, 0, 0, 0, width, height, depth,
external_format, image_size, image);
}
} else {
report_my_gl_errors();
return false;
}
break;
default:
if (image_compression == Texture::CM_off) {
GLP(TexSubImage2D)(target, n, 0, 0, width, height,
external_format, component_type, image);
} else {
_glCompressedTexSubImage2D(target, n, 0, 0, width, height,
external_format, image_size, image);
}
break;
}
break;
default:
if (image_compression == Texture::CM_off) {
GLP(TexSubImage2D)(target, 0, 0, 0, width, height,
external_format, component_type, image);
} else {
_glCompressedTexSubImage2D(target, 0, 0, 0, width, height,
external_format, image_size, image);
}
break;
highest_level = n;
width = max(width >> 1, 1);
height = max(height >> 1, 1);
depth = max(depth >> 1, 1);
}
}
if (load_ram_mipmaps) {
// By the time we get here, we have successfully loaded a certain
// number of mipmap levels. Tell the GL that's all it's going to
// get.
GLP(TexParameteri)(target, GL_TEXTURE_MAX_LEVEL, highest_level);
} else if (uses_mipmaps) {
// Since the mipmap levels were auto-generated and are therefore
// complete, make sure the GL doesn't remember some previous value
// for GL_TEXTURE_MAX_LEVEL from the above call--set it to the
// full count of mipmap levels.
GLP(TexParameteri)(target, GL_TEXTURE_MAX_LEVEL, tex->get_expected_num_mipmap_levels() - 1);
}
// Report the error message explicitly if the GL texture creation
// failed.
GLenum error_code = GLP(GetError)();
if (error_code != GL_NO_ERROR) {
const GLubyte *error_string = GLUP(ErrorString)(error_code);
GLCAT.error()
<< "GL texture creation failed for " << gtc->get_texture()->get_name();
<< "GL texture creation failed for " << tex->get_name();
if (error_string != (const GLubyte *)NULL) {
GLCAT.error(false)
<< " : " << error_string;
@ -6810,6 +6821,91 @@ check_nonresident_texture(BufferContextChain &chain) {
}
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::extract_texture_image
// Access: Protected
// Description: Called from extract_texture_data(), this gets just
// the image array for a particular mipmap level (or for
// the base image).
////////////////////////////////////////////////////////////////////
bool CLP(GraphicsStateGuardian)::
extract_texture_image(PTA_uchar &image, size_t &page_size,
Texture *tex, GLenum target, GLenum page_target,
Texture::ComponentType type,
Texture::CompressionMode compression, int n) {
if (target == GL_TEXTURE_CUBE_MAP) {
// A cube map, compressed or uncompressed. This we must extract
// one page at a time.
// If the cube map is compressed, we assume that all the
// compressed pages are exactly the same size. OpenGL doesn't
// make this assumption, but it happens to be true for all
// currently extant compression schemes, and it makes things
// simpler for us. (It also makes things much simpler for the
// graphics hardware, so it's likely to continue to be true for a
// while at least.)
GLenum external_format = get_external_image_format(tex);
GLenum pixel_type = get_component_type(type);
page_size = tex->get_expected_ram_mipmap_page_size(n);
if (compression != Texture::CM_off) {
GLint image_size;
GLP(GetTexLevelParameteriv)(page_target, n,
GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size);
nassertr(image_size <= (int)page_size, false);
page_size = image_size;
}
image = PTA_uchar::empty_array(page_size * 6);
for (int z = 0; z < 6; ++z) {
page_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z;
if (compression == Texture::CM_off) {
GLP(GetTexImage)(page_target, n, external_format, pixel_type,
image.p() + z * page_size);
} else {
_glGetCompressedTexImage(page_target, 0, image.p() + z * page_size);
}
}
} else if (compression == Texture::CM_off) {
// An uncompressed 1-d, 2-d, or 3-d texture.
image = PTA_uchar::empty_array(tex->get_expected_ram_mipmap_image_size(n));
GLenum external_format = get_external_image_format(tex);
GLenum pixel_type = get_component_type(type);
GLP(GetTexImage)(target, n, external_format, pixel_type, image.p());
} else {
// A compressed 1-d, 2-d, or 3-d texture.
GLint image_size;
GLP(GetTexLevelParameteriv)(target, n, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &image_size);
page_size = image_size / tex->get_z_size();
image = PTA_uchar::empty_array(image_size);
_glGetCompressedTexImage(target, n, image.p());
}
// Now see if we were successful.
GLenum error_code = GLP(GetError)();
if (error_code != GL_NO_ERROR) {
const GLubyte *error_string = GLUP(ErrorString)(error_code);
GLCAT.error()
<< "Unable to extract texture for " << *tex
<< ", mipmap level " << n << ": ";
if (error_string != (const GLubyte *)NULL) {
GLCAT.error(false)
<< " : " << error_string;
}
GLCAT.error(false)
<< "\n";
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::do_point_size
// Access: Protected
@ -6850,173 +6946,3 @@ do_point_size() {
report_my_gl_errors();
}
#ifndef NDEBUG
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::build_phony_mipmaps
// Access: Protected
// Description: Generates a series of colored mipmap levels to aid in
// visualizing the mipmap levels as the hardware applies
// them.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
build_phony_mipmaps(Texture *tex) {
int x_size = tex->get_x_size();
int y_size = tex->get_y_size();
GLCAT.info()
<< "Building phony mipmap levels for " << tex->get_name() << "\n";
int level = 0;
while (x_size > 0 && y_size > 0) {
GLCAT.info(false)
<< " level " << level << " is " << x_size << " by " << y_size << "\n";
build_phony_mipmap_level(level, x_size, y_size);
x_size >>= 1;
y_size >>= 1;
level++;
}
while (x_size > 0) {
GLCAT.info(false)
<< " level " << level << " is " << x_size << " by 1\n";
build_phony_mipmap_level(level, x_size, 1);
x_size >>= 1;
level++;
}
while (y_size > 0) {
GLCAT.info(false)
<< " level " << level << " is 1 by " << y_size << "\n";
build_phony_mipmap_level(level, 1, y_size);
y_size >>= 1;
level++;
}
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::build_phony_mipmap_level
// Access: Protected
// Description: Generates a single colored mipmap level.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
build_phony_mipmap_level(int level, int x_size, int y_size) {
static const int num_levels = 10;
static const char *level_filenames[num_levels] = {
"mipmap_level_0.rgb",
"mipmap_level_1.rgb",
"mipmap_level_2.rgb",
"mipmap_level_3.rgb",
"mipmap_level_4.rgb",
"mipmap_level_5.rgb",
"mipmap_level_6.rgb",
"mipmap_level_7.rgb",
"mipmap_level_8.rgb",
"mipmap_level_9.rgb"
};
static const RGBColorf level_colors[num_levels] = {
RGBColorf(1.0f, 1.0f, 1.0f),
RGBColorf(1.0f, 0.0f, 0.0f),
RGBColorf(0.0f, 1.0f, 0.0f),
RGBColorf(0.0f, 0.0f, 1.0f),
RGBColorf(1.0f, 1.0f, 0.0f),
RGBColorf(0.0f, 1.0f, 1.0f),
RGBColorf(1.0f, 0.0f, 1.0f),
RGBColorf(1.0f, 0.5, 0.0f),
RGBColorf(0.0f, 1.0f, 0.5),
RGBColorf(0.83, 0.71, 1.0f)
};
level = level % num_levels;
Filename filename(level_filenames[level]);
PNMImage image_sized(x_size, y_size);
PNMImage image_source;
if (filename.resolve_filename(get_texture_path()) ||
filename.resolve_filename(get_model_path())) {
image_source.read(filename);
}
if (image_source.is_valid()) {
image_sized.quick_filter_from(image_source);
} else {
GLCAT.info(false)
<< " " << filename << " cannot be read, making solid color mipmap.\n";
image_sized.fill(level_colors[level][0],
level_colors[level][1],
level_colors[level][2]);
}
PT(Texture) tex = new Texture;
if (!tex->load(image_sized)) {
GLCAT.warning()
<< "Unable to load phony mipmap image.\n";
} else {
GLenum internal_format = get_internal_image_format(tex);
GLenum external_format = get_external_image_format(tex);
GLenum component_type = get_component_type(tex->get_component_type());
GLP(TexImage2D)(GL_TEXTURE_2D, level, internal_format,
tex->get_x_size(), tex->get_y_size(), 0,
external_format, component_type, tex->get_ram_image());
}
}
////////////////////////////////////////////////////////////////////
// Function: GLGraphicsStateGuardian::save_mipmap_images
// Access: Protected
// Description: Saves out each mipmap level of the indicated texture
// (which must also be the currently active texture in
// the GL state) as a separate image file to disk.
////////////////////////////////////////////////////////////////////
void CLP(GraphicsStateGuardian)::
save_mipmap_images(Texture *tex) {
if (tex->get_texture_type() != Texture::TT_2d_texture) {
// Never mind on unusual texture formats.
return;
}
Filename filename = tex->get_name();
string name;
if (filename.empty()) {
static int index = 0;
name = "texture" + format_string(index);
index++;
} else {
name = filename.get_basename_wo_extension();
}
GLenum external_format = get_external_image_format(tex);
GLenum type = get_component_type(tex->get_component_type());
int x_size = tex->get_x_size();
int y_size = tex->get_y_size();
// Specify byte-alignment for the pixels on output.
GLP(PixelStorei)(GL_PACK_ALIGNMENT, 1);
int mipmap_level = 0;
do {
x_size = max(x_size, 1);
y_size = max(y_size, 1);
PT(Texture) mtex = new Texture;
mtex->setup_2d_texture(x_size, y_size, tex->get_component_type(),
tex->get_format());
GLP(GetTexImage)(GL_TEXTURE_2D, mipmap_level, external_format,
type, mtex->make_ram_image());
Filename mipmap_filename = name + "_" + format_string(mipmap_level) + ".rgb";
nout << "Writing mipmap level " << mipmap_level
<< " (" << x_size << " by " << y_size << ") "
<< mipmap_filename << "\n";
mtex->write(mipmap_filename);
x_size >>= 1;
y_size >>= 1;
mipmap_level++;
} while (x_size > 0 || y_size > 0);
}
#endif // NDEBUG

View File

@ -65,11 +65,12 @@ 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 PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data);
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data);
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, 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);
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data);
class CLP(GeomContext);
typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data);
////////////////////////////////////////////////////////////////////
// Class : GLGraphicsStateGuardian
@ -274,21 +275,18 @@ protected:
GLenum target, GLint internal_format,
int width, int height, int depth,
GLint external_format, GLenum component_type,
const unsigned char *image,
size_t image_size,
bool one_page_only, int z,
Texture::CompressionMode image_compression);
size_t get_texture_memory_size(Texture *tex);
void check_nonresident_texture(BufferContextChain &chain);
bool extract_texture_image(PTA_uchar &image, size_t &page_size,
Texture *tex, GLenum target, GLenum page_target,
Texture::ComponentType type,
Texture::CompressionMode compression, int n);
void do_point_size();
#ifndef NDEBUG
void build_phony_mipmaps(Texture *tex);
void build_phony_mipmap_level(int level, int x_size, int y_size);
void save_mipmap_images(Texture *tex);
#endif
enum AutoAntialiasMode {
AA_poly,
AA_line,
@ -374,8 +372,12 @@ public:
PFNGLTEXIMAGE3DPROC _glTexImage3D;
PFNGLTEXSUBIMAGE3DPROC _glTexSubImage3D;
PFNGLCOMPRESSEDTEXIMAGE1DPROC _glCompressedTexImage1D;
PFNGLCOMPRESSEDTEXIMAGE2DPROC _glCompressedTexImage2D;
PFNGLCOMPRESSEDTEXIMAGE3DPROC _glCompressedTexImage3D;
PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC _glCompressedTexSubImage1D;
PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC _glCompressedTexSubImage2D;
PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC _glCompressedTexSubImage3D;
PFNGLGETCOMPRESSEDTEXIMAGEPROC _glGetCompressedTexImage;
bool _supports_bgr;

View File

@ -56,16 +56,6 @@ ConfigVariableBool CLP(force_mipmaps)
PRC_DESC("Configure this true to enable full trilinear mipmapping on every "
"texture, whether it asks for it or not."));
ConfigVariableBool CLP(show_mipmaps)
("gl-show-mipmaps", false,
PRC_DESC("Configure this true to cause mipmaps to be rendered with phony "
"colors, using mipmap_level_*.rgb if they are available."));
ConfigVariableBool CLP(save_mipmaps)
("gl-save-mipmaps", false,
PRC_DESC("Configure this true to cause the generated mipmap images to be "
"written out to image files on the disk as they are generated."));
ConfigVariableBool CLP(color_mask)
("gl-color-mask", true,
PRC_DESC("Configure this false if your GL's implementation of glColorMask() "

View File

@ -27,8 +27,6 @@ extern ConfigVariableBool CLP(support_clamp_to_border);
extern ConfigVariableBool CLP(ignore_filters);
extern ConfigVariableBool CLP(ignore_mipmaps);
extern ConfigVariableBool CLP(force_mipmaps);
extern ConfigVariableBool CLP(show_mipmaps);
extern ConfigVariableBool CLP(save_mipmaps);
extern ConfigVariableBool CLP(color_mask);
extern ConfigVariableBool CLP(compile_and_execute);

View File

@ -126,6 +126,240 @@ setup_cube_map(int size, ComponentType component_type,
setup_texture(TT_cube_map, size, size, 6, component_type, format);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read
// Access: Published
// Description: Reads the named filename into the texture.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
read(const Filename &fullpath) {
return do_read(fullpath, Filename(), 0, 0, 0, 0, false, false);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read
// Access: Published
// Description: Combine a 3-component image with a grayscale image
// to get a 4-component image.
//
// See the description of the full-parameter read()
// method for the meaning of the
// primary_file_num_channels and alpha_file_channel
// parameters.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
read(const Filename &fullpath, const Filename &alpha_fullpath,
int primary_file_num_channels, int alpha_file_channel) {
return do_read(fullpath, alpha_fullpath, primary_file_num_channels,
alpha_file_channel, 0, 0, false, false);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read
// Access: Published
// Description: Reads a single file into a single page or mipmap
// level, or automatically reads a series of files into
// a series of pages and/or mipmap levels.
//
// See the description of the full-parameter read()
// method for the meaning of the various parameters.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
read(const Filename &fullpath, int z, int n,
bool read_pages, bool read_mipmaps) {
return do_read(fullpath, Filename(), 0, 0, z, n, read_pages, read_mipmaps);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::read
// Access: Published
// Description: Reads the texture from the indicated filename. If
// primary_file_num_channels is not 0, it specifies the
// number of components to downgrade the image to if it
// is greater than this number.
//
// If the filename has the extension .txo, this
// implicitly reads a texture object instead of a
// filename (which replaces all of the texture
// properties). In this case, all the rest of the
// parameters are ignored, and the filename should not
// contain any hash marks; just the one named file will
// be read, since a single .txo file can contain all
// pages and mipmaps necessary to define a texture.
//
// If alpha_fullpath is not empty, it specifies the name
// of a file from which to retrieve the alpha. In this
// case, alpha_file_channel represents the numeric
// channel of this image file to use as the resulting
// texture's alpha channel; usually, this is 0 to
// indicate the grayscale combination of r, g, b; or it
// may be a one-based channel number, e.g. 1 for the red
// channel, 2 for the green channel, and so on.
//
// If read pages is false, then z indicates the page
// number into which this image will be assigned.
// Normally this is 0 for the first (or only) page of
// the texture. 3-D textures have one page for each
// level of depth, and cube map textures always have six
// pages.
//
// If read_pages is true, multiple images will be read
// at once, one for each page of a cube map or a 3-D
// texture. In this case, the filename should contain a
// sequence of one or more hash marks ("#") which will
// be filled in with the z value of each page,
// zero-based. In this case, the z parameter indicates
// the maximum z value that will be loaded, or 0 to load
// all filenames that exist.
//
// If read_mipmaps is false, then n indicates the mipmap
// level to which this image will be assigned. Normally
// this is 0 for the base texture image, but it is
// possible to load custom mipmap levels into the later
// images. After the base texture image is loaded (thus
// defining the size of the texture), you can call
// get_expected_num_mipmap_levels() to determine the
// maximum sensible value for n.
//
// If read_mipmaps is true, multiple images will be read
// as above, but this time the images represent the
// different mipmap levels of the texture image. In
// this case, the n parameter indicates the maximum n
// value that will be loaded, or 0 to load all filenames
// that exist (up to the expected number of mipmap
// levels).
//
// If both read_pages and read_mipmaps is true, then
// both sequences will be read; the filename should
// contain two sequences of hash marks, separated by
// some character such as a hyphen, underscore, or dot.
// The first hash mark sequence will be filled in with
// the mipmap level, while the second hash mark sequence
// will be the page index.
//
// This method implicitly sets keep_ram_image to false.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
read(const Filename &fullpath, const Filename &alpha_fullpath,
int primary_file_num_channels, int alpha_file_channel,
int z, int n, bool read_pages, bool read_mipmaps) {
return do_read(fullpath, alpha_fullpath, primary_file_num_channels,
alpha_file_channel, z, n, read_pages, read_mipmaps);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::write
// Access: Published
// Description: Writes the texture to the named filename.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
write(const Filename &fullpath) {
return do_write(fullpath, 0, 0, false, false);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::write
// Access: Published
// Description: Writes a single page or mipmap level to a single
// file, or automatically writes a series of pages
// and/or mipmap levels to a numbered series of files.
//
// If the filename ends in the extension .txo, this
// implicitly writes a Panda texture object (.txo)
// instead of an image file. In this case, the
// remaining parameters are ignored, and only one file
// is written, which will contain all of the pages and
// resident mipmap levels in the texture.
//
// If write_pages is false, then z indicates the page
// number to write. 3-D textures have one page number
// for each level of depth; cube maps have six pages
// number 0 through 5. Other kinds of textures have
// only one page, numbered 0.
//
// If write_pages is true, then all pages of the texture
// will be written. In this case z is ignored, and the
// filename should contain a sequence of hash marks
// ("#") which will be filled in with the page index
// number.
//
// If write_mipmaps is false, then n indicates the
// mipmap level number to write. Normally, this is 0,
// for the base texture image. Normally, the mipmap
// levels of a texture are not available in RAM (they
// are generated automatically by the graphics card).
// However, if you have the mipmap levels available, for
// instance because you called
// generate_ram_mipmap_images() to generate them
// internally, or you called
// GraphicsEngine::extract_texture_data() to retrieve
// them from the graphics card, then you may write out
// each mipmap level with this parameter.
//
// If write_mipmaps is true, then all mipmap levels of
// the texture will be written. In this case n is
// ignored, and the filename should contain a sequence
// of hash marks ("#") which will be filled in with the
// mipmap level number.
//
// If both write_pages and write_mipmaps is true, then
// all pages and all mipmap levels will be written. In
// this case, the filename should contain two different
// sequences of hash marks, separated by a character
// such as a hyphen, underscore, or dot. The first hash
// mark sequence will be filled in with the mipmap
// level, while the second hash mark sequence will be
// the page index.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
write(const Filename &fullpath, int z, int n,
bool write_pages, bool write_mipmaps) {
return do_write(fullpath, z, n, write_pages, write_mipmaps);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::load
// Access: Published
// Description: Replaces the texture with the indicated image.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
load(const PNMImage &pnmimage) {
return do_load_one(pnmimage, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::load
// Access: Published
// Description: Stores the indicated image in the given page and
// mipmap level. See read().
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
load(const PNMImage &pnmimage, int z, int n) {
return do_load_one(pnmimage, z, n);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::store
// Access: Published
// Description: Saves the texture to the indicated PNMImage, but does
// not write it to disk.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
store(PNMImage &pnmimage) const {
return do_store_one(pnmimage, 0, 0);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::store
// Access: Published
// Description: Saves the indicated page and mipmap level of the
// texture to the PNMImage.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
store(PNMImage &pnmimage, int z, int n) const {
return do_store_one(pnmimage, z, n);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::has_filename
// Access: Published
@ -462,7 +696,10 @@ might_have_ram_image() const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_image_size() const {
return _ram_image.size();
if (_ram_images.empty()) {
return 0;
}
return _ram_images[0]._image.size();
}
////////////////////////////////////////////////////////////////////
@ -479,7 +716,11 @@ get_ram_image_size() const {
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_page_size() const {
return _ram_image_compression != CM_off ? _ram_page_size : get_expected_ram_page_size();
if (_ram_image_compression == CM_off || _ram_images.empty()) {
return get_expected_ram_page_size();
} else {
return _ram_images[0]._page_size;
}
}
////////////////////////////////////////////////////////////////////
@ -542,6 +783,99 @@ set_keep_ram_image(bool keep_ram_image) {
_keep_ram_image = keep_ram_image;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_num_ram_mipmap_images
// Access: Published
// Description: Returns the maximum number of mipmap level images
// available in system memory. The actual number may be
// less than this; use has_ram_mipmap_image() to verify
// each level.
////////////////////////////////////////////////////////////////////
INLINE int Texture::
get_num_ram_mipmap_images() const {
return _ram_images.size();
}
////////////////////////////////////////////////////////////////////
// Function: Texture::has_ram_mipmap_image
// Access: Published
// Description: Returns true if the Texture has the nth mipmap level
// available in system memory, false otherwise. If the
// texture's minfilter mode requires mipmapping (see
// uses_mipmaps()), and all the texture's mipmap levels
// are not available when the texture is rendered, they
// will be generated automatically.
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
has_ram_mipmap_image(int n) const {
return (n >= 0 && n < (int)_ram_images.size() && !_ram_images[n]._image.empty());
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_ram_mipmap_image_size
// Access: Published
// Description: Returns the number of bytes used by the in-memory
// image for mipmap level n, or 0 if there is no
// in-memory image for this mipmap level.
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_ram_mipmap_image_size(int n) const {
if (n >= 0 && n < (int)_ram_images.size()) {
return _ram_images[n]._image.size();
}
return 0;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_ram_mipmap_page_size
// Access: Published
// Description: Returns the number of bytes used by the in-memory
// image per page for mipmap level n, or 0 if there is
// no in-memory image for this mipmap level.
//
// For a non-compressed texture, this is the same as
// get_expected_ram_mipmap_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_mipmap_page_size(int n) const {
if (_ram_image_compression != CM_off) {
if (n >= 0 && n < (int)_ram_images.size()) {
return _ram_images[n]._page_size;
}
return 0;
} else {
return get_expected_ram_mipmap_page_size(n);
}
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_expected_ram_mipmap_image_size
// Access: Published
// Description: Returns the number of bytes that *ought* to be used
// by the in-memory image for mipmap level n, based on
// the texture parameters.
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_expected_ram_mipmap_image_size(int n) const {
return get_expected_ram_mipmap_page_size(n) * (size_t)get_expected_mipmap_z_size(n);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_expected_ram_mipmap_page_size
// Access: Published
// Description: Returns the number of bytes that should be used per
// each Z page of the 3-d texture, for mipmap level n.
// For a 2-d or 1-d texture, this is the same as
// get_expected_ram_mipmap_image_size(n).
////////////////////////////////////////////////////////////////////
INLINE size_t Texture::
get_expected_ram_mipmap_page_size(int n) const {
return (size_t)(get_expected_mipmap_x_size(n) * get_expected_mipmap_y_size(n) * _num_components * _component_width);
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_modified
// Access: Published
@ -714,28 +1048,29 @@ set_z_size(int z_size) {
}
////////////////////////////////////////////////////////////////////
// Function: Texture::set_loaded_from_disk
// Function: Texture::set_loaded_from_image
// Access: Published
// Description: Sets the flag that indicates the texture has been
// loaded from a disk file. You should also ensure the
// filename has been set correctly. When this flag is
// true, the texture may be automatically reloaded when
// its ram image needs to be replaced.
// loaded from a disk file or PNMImage. You should also
// ensure the filename has been set correctly. When
// this flag is true, the texture may be automatically
// reloaded when its ram image needs to be replaced.
////////////////////////////////////////////////////////////////////
INLINE void Texture::
set_loaded_from_disk() {
_loaded_from_disk = true;
set_loaded_from_image() {
_loaded_from_image = true;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_loaded_from_disk
// Function: Texture::get_loaded_from_image
// Access: Published
// Description: Returns the flag that indicates the texture has been
// loaded from a disk file. See set_loaded_from_disk().
// loaded from a disk file or PNMImage. See
// set_loaded_from_image().
////////////////////////////////////////////////////////////////////
INLINE bool Texture::
get_loaded_from_disk() const {
return _loaded_from_disk;
get_loaded_from_image() const {
return _loaded_from_image;
}
////////////////////////////////////////////////////////////////////
@ -800,7 +1135,7 @@ set_match_framebuffer_format(bool flag) {
////////////////////////////////////////////////////////////////////
// Function: Texture::store_unscaled_byte
// Access: Private
// Access: Private, Static
// Description: This is used by load() to store the next consecutive
// component value into the indicated element of the
// array, which is taken to be an array of unsigned
@ -808,13 +1143,13 @@ set_match_framebuffer_format(bool flag) {
// 0-255.
////////////////////////////////////////////////////////////////////
INLINE void Texture::
store_unscaled_byte(int &index, int value) {
_ram_image[index++] = (uchar)value;
store_unscaled_byte(unsigned char *&p, int value) {
(*p++) = (uchar)value;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::store_unscaled_short
// Access: Private
// Access: Private, Static
// Description: This is used by load() to store the next consecutive
// component value into the indicated element of the
// array, which is taken to be an array of unsigned
@ -822,19 +1157,19 @@ store_unscaled_byte(int &index, int value) {
// 0-65535.
////////////////////////////////////////////////////////////////////
INLINE void Texture::
store_unscaled_short(int &index, int value) {
store_unscaled_short(unsigned char *&p, int value) {
union {
ushort us;
uchar uc[2];
} v;
v.us = (ushort)value;
_ram_image[index++] = v.uc[0];
_ram_image[index++] = v.uc[1];
(*p++) = v.uc[0];
(*p++) = v.uc[1];
}
////////////////////////////////////////////////////////////////////
// Function: Texture::store_scaled_byte
// Access: Private
// Access: Private, Static
// Description: This is used by load() to store the next consecutive
// component value into the indicated element of the
// array, which is taken to be an array of unsigned
@ -842,13 +1177,13 @@ store_unscaled_short(int &index, int value) {
// factor before storing it.
////////////////////////////////////////////////////////////////////
INLINE void Texture::
store_scaled_byte(int &index, int value, double scale) {
store_unscaled_byte(index, (int)(value * scale));
store_scaled_byte(unsigned char *&p, int value, double scale) {
store_unscaled_byte(p, (int)(value * scale));
}
////////////////////////////////////////////////////////////////////
// Function: Texture::store_scaled_short
// Access: Private
// Access: Private, Static
// Description: This is used by load() to store the next consecutive
// component value into the indicated element of the
// array, which is taken to be an array of unsigned
@ -856,41 +1191,39 @@ store_scaled_byte(int &index, int value, double scale) {
// factor before storing it.
////////////////////////////////////////////////////////////////////
INLINE void Texture::
store_scaled_short(int &index, int value, double scale) {
store_unscaled_short(index, (int)(value * scale));
store_scaled_short(unsigned char *&p, int value, double scale) {
store_unscaled_short(p, (int)(value * scale));
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_unsigned_byte
// Access: Private
// Description: This is used by store() to retieve the next
// Access: Private, Static
// Description: This is used by store() to retrieve the next
// consecutive component value from the indicated
// element of the array, which is taken to be an array
// of unsigned bytes.
////////////////////////////////////////////////////////////////////
INLINE double Texture::
get_unsigned_byte(int &index) const {
nassertr(index >= 0 && index < (int)_ram_image.size(), 0.0);
return (double)_ram_image[index++] / 255.0;
get_unsigned_byte(const unsigned char *&p) {
return (double)(*p++) / 255.0;
}
////////////////////////////////////////////////////////////////////
// Function: Texture::get_unsigned_short
// Access: Private
// Description: This is used by store() to retieve the next
// Access: Private, Static
// Description: This is used by store() to retrieve the next
// consecutive component value from the indicated
// element of the array, which is taken to be an array
// of unsigned shorts.
////////////////////////////////////////////////////////////////////
INLINE double Texture::
get_unsigned_short(int &index) const {
nassertr(index >= 0 && index+1 < (int)_ram_image.size(), 0.0);
get_unsigned_short(const unsigned char *&p) {
union {
ushort us;
uchar uc[2];
} v;
v.uc[0] = _ram_image[index++];
v.uc[1] = _ram_image[index++];
v.uc[0] = (*p++);
v.uc[1] = (*p++);
return (double)v.us / 65535.0;
}

File diff suppressed because it is too large Load Diff

View File

@ -146,7 +146,7 @@ PUBLISHED:
};
enum CompressionMode {
// Generic compresssion modes. Usually, you should choose one of
// Generic compression modes. Usually, you should choose one of
// these.
CM_default, // on or off, according to compressed-textures
CM_off, // uncompressed image
@ -174,6 +174,7 @@ PUBLISHED:
virtual ~Texture();
virtual PT(Texture) make_copy();
virtual void clear();
void setup_texture(TextureType texture_type,
int x_size, int y_size, int z_size,
@ -194,21 +195,26 @@ PUBLISHED:
void generate_normalization_cube_map(int size);
virtual bool read(const Filename &fullpath, int z = 0,
int primary_file_num_channels = 0);
virtual bool read(const Filename &fullpath, const Filename &alpha_fullpath,
int z = 0,
int primary_file_num_channels = 0, int alpha_file_channel = 0);
bool write(const Filename &fullpath, int z = 0) const;
INLINE bool read(const Filename &fullpath);
INLINE bool read(const Filename &fullpath, const Filename &alpha_fullpath,
int primary_file_num_channels, int alpha_file_channel);
INLINE bool read(const Filename &fullpath, int z, int n,
bool read_pages, bool read_mipmaps);
INLINE bool read(const Filename &fullpath, const Filename &alpha_fullpath,
int primary_file_num_channels, int alpha_file_channel,
int z, int n, bool read_pages, bool read_mipmaps);
INLINE bool write(const Filename &fullpath);
INLINE bool write(const Filename &fullpath, int z, int n,
bool write_pages, bool write_mipmaps);
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);
virtual bool load(const PNMImage &pnmimage, int z = 0);
bool store(PNMImage &pnmimage, int z = 0) const;
INLINE bool load(const PNMImage &pnmimage);
INLINE bool load(const PNMImage &pnmimage, int z, int n);
INLINE bool store(PNMImage &pnmimage) const;
INLINE bool store(PNMImage &pnmimage, int z, int n) const;
Texture *load_related(const PT(InternalName) &suffix) const;
@ -252,6 +258,11 @@ PUBLISHED:
INLINE bool get_render_to_texture() const;
INLINE bool uses_mipmaps() const;
int get_expected_num_mipmap_levels() const;
int get_expected_mipmap_x_size(int n) const;
int get_expected_mipmap_y_size(int n) const;
int get_expected_mipmap_z_size(int n) const;
virtual bool has_ram_image() const;
INLINE bool might_have_ram_image() const;
INLINE size_t get_ram_image_size() const;
@ -268,6 +279,21 @@ PUBLISHED:
INLINE void set_keep_ram_image(bool keep_ram_image);
virtual bool get_keep_ram_image() const;
INLINE int get_num_ram_mipmap_images() const;
INLINE bool has_ram_mipmap_image(int n) const;
bool has_all_ram_mipmap_images() const;
INLINE size_t get_ram_mipmap_image_size(int n) const;
INLINE size_t get_ram_mipmap_page_size(int n) const;
INLINE size_t get_expected_ram_mipmap_image_size(int n) const;
INLINE size_t get_expected_ram_mipmap_page_size(int n) const;
CPTA_uchar get_ram_mipmap_image(int n);
PTA_uchar modify_ram_mipmap_image(int n);
PTA_uchar make_ram_mipmap_image(int n);
void set_ram_mipmap_image(int n, PTA_uchar image, size_t page_size = 0);
void clear_ram_mipmap_image(int n);
void clear_ram_mipmap_images();
void generate_ram_mipmap_images();
INLINE UpdateSeq get_modified() const;
void prepare(PreparedGraphicsObjects *prepared_objects);
@ -297,18 +323,18 @@ PUBLISHED:
INLINE void set_z_size(int z_size);
void set_format(Format format);
void set_component_type(ComponentType component_type);
INLINE void set_loaded_from_disk();
INLINE bool get_loaded_from_disk() const;
INLINE void set_loaded_from_image();
INLINE bool get_loaded_from_image() const;
INLINE void set_loaded_from_txo();
INLINE bool get_loaded_from_txo() const;
static bool is_mipmap(FilterType type);
public:
INLINE bool get_match_framebuffer_format() const;
INLINE void set_match_framebuffer_format(bool flag);
static bool is_mipmap(FilterType type);
TextureContext *prepare_now(PreparedGraphicsObjects *prepared_objects,
GraphicsStateGuardianBase *gsg);
@ -328,6 +354,18 @@ public:
static bool has_alpha(Format format);
protected:
virtual bool do_read(const Filename &fullpath, const Filename &alpha_fullpath,
int primary_file_num_channels, int alpha_file_channel,
int z, int n, bool read_pages, bool read_mipmaps);
virtual bool do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
int z, int n, int primary_file_num_channels, int alpha_file_channel);
bool do_write(const Filename &fullpath, int z, int n,
bool write_pages, bool write_mipmaps) const;
bool do_write_one(const Filename &fullpath, int z, int n) const;
virtual bool do_load_one(const PNMImage &pnmimage, int z, int n);
bool do_store_one(PNMImage &pnmimage, int z, int n) const;
virtual void reconsider_dirty();
virtual void reload_ram_image();
@ -335,23 +373,65 @@ protected:
bool reconsider_image_properties(int x_size, int y_size, int num_components,
ComponentType component_type, int z);
// This nested class declaration is used below.
class RamImage {
public:
PTA_uchar _image;
size_t _page_size;
};
private:
void convert_from_pnmimage(PTA_uchar &image, size_t page_size, int z,
const PNMImage &pnmimage);
bool convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
CPTA_uchar image, size_t page_size, int z) const;
void clear_prepared(PreparedGraphicsObjects *prepared_objects);
void consider_rescale(PNMImage &pnmimage);
void consider_downgrade(PNMImage &pnmimage, int num_channels);
INLINE void store_unscaled_byte(int &index, int value);
INLINE void store_unscaled_short(int &index, int value);
INLINE void store_scaled_byte(int &index, int value, double scale);
INLINE void store_scaled_short(int &index, int value, double scale);
INLINE double get_unsigned_byte(int &index) const;
INLINE double get_unsigned_short(int &index) const;
INLINE static void store_unscaled_byte(unsigned char *&p, int value);
INLINE static void store_unscaled_short(unsigned char *&p, int value);
INLINE static void store_scaled_byte(unsigned char *&p, int value, double scale);
INLINE static void store_scaled_short(unsigned char *&p, int value, double scale);
INLINE static double get_unsigned_byte(const unsigned char *&p);
INLINE static double get_unsigned_short(const unsigned char *&p);
INLINE static bool is_txo_filename(const Filename &fullpath);
bool read_txo_file(const Filename &fullpath);
bool write_txo_file(const Filename &fullpath) const;
void filter_2d_mipmap_pages(RamImage &to, const RamImage &from,
int x_size, int y_size);
void filter_3d_mipmap_level(RamImage &to, const RamImage &from,
int x_size, int y_size, int z_size);
typedef void Filter2DComponent(unsigned char *&p,
const unsigned char *&q,
size_t pixel_size, size_t row_size);
typedef void Filter3DComponent(unsigned char *&p,
const unsigned char *&q,
size_t pixel_size, size_t row_size,
size_t page_size);
static void filter_2d_unsigned_byte(unsigned char *&p,
const unsigned char *&q,
size_t pixel_size, size_t row_size);
static void filter_2d_unsigned_short(unsigned char *&p,
const unsigned char *&q,
size_t pixel_size, size_t row_size);
static void filter_3d_unsigned_byte(unsigned char *&p,
const unsigned char *&q,
size_t pixel_size, size_t row_size,
size_t page_size);
static void filter_3d_unsigned_short(unsigned char *&p,
const unsigned char *&q,
size_t pixel_size, size_t row_size,
size_t page_size);
protected:
Filename _filename;
Filename _alpha_filename;
@ -376,8 +456,11 @@ protected:
Format _format;
ComponentType _component_type;
bool _loaded_from_disk;
bool _loaded_from_image;
bool _loaded_from_txo;
bool _has_read_pages;
bool _has_read_mipmaps;
int _num_mipmap_levels_read;
WrapMode _wrap_u;
WrapMode _wrap_v;
@ -407,9 +490,13 @@ protected:
typedef pmap<PT(InternalName), PT(Texture)> RelatedTextures;
RelatedTextures _related_textures;
PTA_uchar _ram_image;
CompressionMode _ram_image_compression;
size_t _ram_page_size;
// There is usually one RamImage for the mipmap level 0 (the base
// image). There may or may not also be additional images for the
// additional mipmap levels.
typedef pvector<RamImage> RamImages;
RamImages _ram_images;
UpdateSeq _modified;

View File

@ -51,10 +51,17 @@ verify_texture(const string &filename) {
// If a texture with the same filename was previously
// loaded, returns that one instead. If the texture
// file cannot be found, returns NULL.
//
// If read_mipmaps is true, the filename should contain
// a hash mark ('#'), which will be filled in with the
// mipmap level number; and the texture will be defined
// with a series of images, one for each mipmap level.
////////////////////////////////////////////////////////////////////
INLINE Texture *TexturePool::
load_texture(const string &filename, int primary_file_num_channels) {
return get_global_ptr()->ns_load_texture(filename, primary_file_num_channels);
load_texture(const string &filename, int primary_file_num_channels,
bool read_mipmaps) {
return get_global_ptr()->ns_load_texture(filename, primary_file_num_channels,
read_mipmaps);
}
////////////////////////////////////////////////////////////////////
@ -65,13 +72,21 @@ load_texture(const string &filename, int primary_file_num_channels) {
// If a texture with the same filename was previously
// loaded, returns that one instead. If the texture
// file cannot be found, returns NULL.
//
// If read_mipmaps is true, both filenames should
// contain a hash mark ('#'), which will be filled in
// with the mipmap level number; and the texture will be
// defined with a series of images, two for each mipmap
// level.
////////////////////////////////////////////////////////////////////
INLINE Texture *TexturePool::
load_texture(const string &filename, const string &alpha_filename,
int primary_file_num_channels, int alpha_file_channel) {
int primary_file_num_channels, int alpha_file_channel,
bool read_mipmaps) {
return get_global_ptr()->ns_load_texture(filename, alpha_filename,
primary_file_num_channels,
alpha_file_channel);
primary_file_num_channels,
alpha_file_channel,
read_mipmaps);
}
////////////////////////////////////////////////////////////////////
@ -82,10 +97,15 @@ load_texture(const string &filename, const string &alpha_filename,
// with index 0. The filename should include a sequence
// of one or more hash characters ("#") which will be
// filled in with the index number of each level.
//
// If read_mipmaps is true, the filename should contain
// an additional hash mark. The first hash mark will be
// filled in with the mipmap level number, and the
// second with the index number of each 3-d level.
////////////////////////////////////////////////////////////////////
INLINE Texture *TexturePool::
load_3d_texture(const string &filename_pattern) {
return get_global_ptr()->ns_load_3d_texture(filename_pattern);
load_3d_texture(const string &filename_pattern, bool read_mipmaps) {
return get_global_ptr()->ns_load_3d_texture(filename_pattern, read_mipmaps);
}
////////////////////////////////////////////////////////////////////
@ -96,10 +116,15 @@ load_3d_texture(const string &filename_pattern) {
// filename should include a sequence of one or more
// hash characters ("#") which will be filled in with
// the index number of each pagee.
//
// If read_mipmaps is true, the filename should contain
// an additional hash mark. The first hash mark will be
// filled in with the mipmap level number, and the
// second with the face number, 0 through 5.
////////////////////////////////////////////////////////////////////
INLINE Texture *TexturePool::
load_cube_map(const string &filename_pattern) {
return get_global_ptr()->ns_load_cube_map(filename_pattern);
load_cube_map(const string &filename_pattern, bool read_mipmaps) {
return get_global_ptr()->ns_load_cube_map(filename_pattern, read_mipmaps);
}
////////////////////////////////////////////////////////////////////

View File

@ -206,7 +206,8 @@ ns_has_texture(const Filename &orig_filename) {
// Description: The nonstatic implementation of load_texture().
////////////////////////////////////////////////////////////////////
Texture *TexturePool::
ns_load_texture(const Filename &orig_filename, int primary_file_num_channels) {
ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
bool read_mipmaps) {
Filename filename(orig_filename);
if (!_fake_texture_image.empty()) {
@ -227,7 +228,7 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels) {
gobj_cat.info()
<< "Loading texture " << filename << "\n";
PT(Texture) tex = make_texture(filename.get_extension());
if (!tex->read(filename, 0, primary_file_num_channels)) {
if (!tex->read(filename, 0, primary_file_num_channels, false, read_mipmaps)) {
// This texture was not found or could not be read.
report_texture_unreadable(filename);
return NULL;
@ -249,12 +250,14 @@ Texture *TexturePool::
ns_load_texture(const Filename &orig_filename,
const Filename &orig_alpha_filename,
int primary_file_num_channels,
int alpha_file_channel) {
int alpha_file_channel,
bool read_mipmaps) {
Filename filename(orig_filename);
Filename alpha_filename(orig_alpha_filename);
if (!_fake_texture_image.empty()) {
return ns_load_texture(_fake_texture_image, primary_file_num_channels);
return ns_load_texture(_fake_texture_image, primary_file_num_channels,
read_mipmaps);
}
VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
@ -275,8 +278,8 @@ ns_load_texture(const Filename &orig_filename,
<< "Loading texture " << filename << " and alpha component "
<< alpha_filename << endl;
PT(Texture) tex = make_texture(filename.get_extension());
if (!tex->read(filename, alpha_filename, 0, primary_file_num_channels,
alpha_file_channel)) {
if (!tex->read(filename, alpha_filename, primary_file_num_channels,
alpha_file_channel, 0, 0, false, read_mipmaps)) {
// This texture was not found or could not be read.
report_texture_unreadable(filename);
return NULL;
@ -296,7 +299,8 @@ ns_load_texture(const Filename &orig_filename,
// Description: The nonstatic implementation of load_3d_texture().
////////////////////////////////////////////////////////////////////
Texture *TexturePool::
ns_load_3d_texture(const Filename &filename_pattern) {
ns_load_3d_texture(const Filename &filename_pattern,
bool read_mipmaps) {
Filename filename(filename_pattern);
filename.set_pattern(true);
@ -315,7 +319,7 @@ ns_load_3d_texture(const Filename &filename_pattern) {
<< "Loading 3-d texture " << filename << "\n";
PT(Texture) tex = make_texture(filename.get_extension());
tex->setup_3d_texture();
if (!tex->read_pages(filename)) {
if (!tex->read(filename, 0, 0, true, read_mipmaps)) {
// This texture was not found or could not be read.
report_texture_unreadable(filename);
return NULL;
@ -334,7 +338,7 @@ ns_load_3d_texture(const Filename &filename_pattern) {
// Description: The nonstatic implementation of load_cube_map().
////////////////////////////////////////////////////////////////////
Texture *TexturePool::
ns_load_cube_map(const Filename &filename_pattern) {
ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps) {
Filename filename(filename_pattern);
filename.set_pattern(true);
@ -353,7 +357,7 @@ ns_load_cube_map(const Filename &filename_pattern) {
<< "Loading cube map texture " << filename << "\n";
PT(Texture) tex = make_texture(filename.get_extension());
tex->setup_cube_map();
if (!tex->read_pages(filename)) {
if (!tex->read(filename, 0, 0, true, read_mipmaps)) {
// This texture was not found or could not be read.
report_texture_unreadable(filename);
return NULL;

View File

@ -42,13 +42,17 @@ PUBLISHED:
INLINE static bool has_texture(const string &filename);
INLINE static bool verify_texture(const string &filename);
INLINE static Texture *load_texture(const string &filename,
int primary_file_num_channels = 0);
int primary_file_num_channels = 0,
bool read_mipmaps = false);
INLINE static Texture *load_texture(const string &filename,
const string &alpha_filename,
int primary_file_num_channels = 0,
int alpha_file_channel = 0);
INLINE static Texture *load_3d_texture(const string &filename_pattern);
INLINE static Texture *load_cube_map(const string &filename_pattern);
int alpha_file_channel = 0,
bool read_mipmaps = false);
INLINE static Texture *load_3d_texture(const string &filename_pattern,
bool read_mipmaps = false);
INLINE static Texture *load_cube_map(const string &filename_pattern,
bool read_mipmaps = false);
INLINE static Texture *get_normalization_cube_map(int size);
@ -81,13 +85,18 @@ private:
TexturePool();
bool ns_has_texture(const Filename &orig_filename);
Texture *ns_load_texture(const Filename &orig_filename, int primary_file_num_channels);
Texture *ns_load_texture(const Filename &orig_filename,
int primary_file_num_channels,
bool read_mipmaps);
Texture *ns_load_texture(const Filename &orig_filename,
const Filename &orig_alpha_filename,
int primary_file_num_channels,
int alpha_file_channel);
Texture *ns_load_3d_texture(const Filename &filename_pattern);
Texture *ns_load_cube_map(const Filename &filename_pattern);
int alpha_file_channel,
bool read_mipmaps);
Texture *ns_load_3d_texture(const Filename &filename_pattern,
bool read_mipmaps);
Texture *ns_load_cube_map(const Filename &filename_pattern,
bool read_mipmaps);
Texture *ns_get_normalization_cube_map(int size);
void ns_add_texture(Texture *texture);

View File

@ -263,13 +263,10 @@ get_alpha_scale() const {
// Function: TextureStage::set_saved_result
// Access: Published
// Description: Sets the saved_result flag. When this is true, the
// output of this stage is not part of the normal
// pipeline--that is, it will not be supplied as the
// "previous" source for the next texture stage--but it
// will instead be supplied as the "last_saved_result"
// source for any future stages, until the next
// TextureStage with a saved_result set true is
// encountered.
// output of this stage will be supplied as the
// "last_saved_result" source for any future stages,
// until the next TextureStage with a saved_result set
// true is encountered.
//
// This can be used to reuse the results of this texture
// stage as input to more than one stage later in the

View File

@ -72,7 +72,7 @@ has_ram_image() const {
if (this_frame != _last_frame_update) {
return false;
}
return !_ram_image.empty();
return !_ram_images.empty() && !_ram_images[0]._image.empty();
}
////////////////////////////////////////////////////////////////////
@ -85,7 +85,7 @@ has_ram_image() const {
////////////////////////////////////////////////////////////////////
bool VideoTexture::
get_keep_ram_image() const {
// An VideoTexture should never dump its RAM image.
// A VideoTexture should never dump its RAM image.
return true;
}

View File

@ -103,136 +103,12 @@ from_camera(int camera_index, int z) {
return false;
}
set_loaded_from_disk();
set_loaded_from_image();
clear_current_frame();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::read
// Access: Published, Virtual
// Description: Takes the video image from the indicated filename.
// If the filename is not a video file, attempts to read
// it as a still image instead.
////////////////////////////////////////////////////////////////////
bool OpenCVTexture::
read(const Filename &fullpath, int z, int) {
if (!reconsider_z_size(z)) {
return false;
}
nassertr(z >= 0 && z < get_z_size(), false);
VideoPage &page = modify_page(z);
page._alpha.clear();
if (!page._color.read(fullpath)) {
grutil_cat.error()
<< "OpenCV couldn't read " << fullpath << " as video.\n";
return false;
}
if (!has_name()) {
set_name(fullpath.get_basename_wo_extension());
}
if (!has_filename()) {
set_filename(fullpath);
clear_alpha_filename();
}
set_fullpath(fullpath);
clear_alpha_fullpath();
_primary_file_num_channels = 3;
_alpha_file_channel = 0;
if (!reconsider_video_properties(page._color, 3, z)) {
page._color.clear();
return false;
}
set_loaded_from_disk();
clear_current_frame();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::read
// Access: Published, Virtual
// Description: Combines a color and alpha video image from the two
// indicated filenames. Both must be the same kind of
// video with similar properties.
////////////////////////////////////////////////////////////////////
bool OpenCVTexture::
read(const Filename &fullpath, const Filename &alpha_fullpath,
int z, int, int alpha_file_channel) {
if (!reconsider_z_size(z)) {
return false;
}
nassertr(z >= 0 && z < get_z_size(), false);
VideoPage &page = modify_page(z);
if (!page._color.read(fullpath)) {
grutil_cat.error()
<< "OpenCV couldn't read " << fullpath << " as video.\n";
return false;
}
if (!page._alpha.read(alpha_fullpath)) {
grutil_cat.error()
<< "OpenCV couldn't read " << alpha_fullpath << " as video.\n";
page._color.clear();
return false;
}
if (!has_name()) {
set_name(fullpath.get_basename_wo_extension());
}
if (!has_filename()) {
set_filename(fullpath);
set_alpha_filename(alpha_fullpath);
}
set_fullpath(fullpath);
set_alpha_fullpath(alpha_fullpath);
_primary_file_num_channels = 3;
_alpha_file_channel = alpha_file_channel;
if (!reconsider_video_properties(page._color, 4, z)) {
page._color.clear();
page._alpha.clear();
return false;
}
if (!reconsider_video_properties(page._alpha, 4, z)) {
page._color.clear();
page._alpha.clear();
return false;
}
set_loaded_from_disk();
clear_current_frame();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::load
// Access: Published, Virtual
// Description: Resets the texture (or the particular level of the
// texture) to the indicated static image.
////////////////////////////////////////////////////////////////////
bool OpenCVTexture::
load(const PNMImage &pnmimage, int z) {
if (z <= (int)_pages.size()) {
VideoPage &page = modify_page(z);
page._color.clear();
page._alpha.clear();
}
return Texture::load(pnmimage, z);
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::modify_page
// Access: Private
@ -295,7 +171,7 @@ reconsider_video_properties(const OpenCVTexture::VideoStream &stream,
return false;
}
if (_loaded_from_disk &&
if (_loaded_from_image &&
(get_video_width() != width || get_video_height() != height ||
get_num_frames() != num_frames || get_frame_rate() != frame_rate)) {
grutil_cat.error()
@ -334,6 +210,7 @@ make_texture() {
////////////////////////////////////////////////////////////////////
void OpenCVTexture::
update_frame(int frame) {
nassertv(has_ram_image());
int max_z = max(_z_size, (int)_pages.size());
for (int z = 0; z < max_z; ++z) {
VideoPage &page = _pages[z];
@ -346,7 +223,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 = _ram_image.p() + get_expected_ram_page_size() * z;
unsigned char *dest = _ram_images[0]._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 +262,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 = _ram_image.p() + get_expected_ram_page_size() * z;
unsigned char *dest = _ram_images[0]._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;
@ -409,6 +286,97 @@ update_frame(int frame) {
}
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::do_read_one
// Access: Protected, Virtual
// Description: Combines a color and alpha video image from the two
// indicated filenames. Both must be the same kind of
// video with similar properties.
////////////////////////////////////////////////////////////////////
bool OpenCVTexture::
do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
int z, int n, int primary_file_num_channels, int alpha_file_channel) {
nassertr(n == 0, false);
nassertr(z >= 0 && z < get_z_size(), false);
VideoPage &page = modify_page(z);
if (!page._color.read(fullpath)) {
grutil_cat.error()
<< "OpenCV couldn't read " << fullpath << " as video.\n";
return false;
}
if (!alpha_fullpath.empty()) {
if (!page._alpha.read(alpha_fullpath)) {
grutil_cat.error()
<< "OpenCV couldn't read " << alpha_fullpath << " as video.\n";
page._color.clear();
return false;
}
}
if (z == 0) {
if (!has_name()) {
set_name(fullpath.get_basename_wo_extension());
}
if (!has_filename()) {
set_filename(fullpath);
set_alpha_filename(alpha_fullpath);
}
set_fullpath(fullpath);
set_alpha_fullpath(alpha_fullpath);
}
_primary_file_num_channels = 3;
_alpha_file_channel = 0;
if (alpha_fullpath.empty()) {
// Only one RGB movie.
if (!reconsider_video_properties(page._color, 3, z)) {
page._color.clear();
return false;
}
} else {
// An RGB movie combined with an alpha movie.
_alpha_file_channel = alpha_file_channel;
if (!reconsider_video_properties(page._color, 4, z)) {
page._color.clear();
page._alpha.clear();
return false;
}
if (!reconsider_video_properties(page._alpha, 4, z)) {
page._color.clear();
page._alpha.clear();
return false;
}
}
set_loaded_from_image();
clear_current_frame();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::do_load_one
// Access: Protected, Virtual
// Description: Resets the texture (or the particular level of the
// texture) to the indicated static image.
////////////////////////////////////////////////////////////////////
bool OpenCVTexture::
do_load_one(const PNMImage &pnmimage, int z, int n) {
if (z <= (int)_pages.size()) {
VideoPage &page = modify_page(z);
page._color.clear();
page._alpha.clear();
}
return Texture::do_load_one(pnmimage, z, n);
}
////////////////////////////////////////////////////////////////////
// Function: OpenCVTexture::register_with_read_factory
// Access: Public, Static

View File

@ -47,19 +47,17 @@ PUBLISHED:
bool from_camera(int camera_index = -1, int z = 0);
virtual bool read(const Filename &fullpath, int z = 0,
int primary_file_num_channels = 0);
virtual bool read(const Filename &fullpath, const Filename &alpha_fullpath,
int z = 0,
int primary_file_num_channels = 0, int alpha_file_channel = 0);
virtual bool load(const PNMImage &pnmimage, int z = 0);
public:
static PT(Texture) make_texture();
protected:
virtual void update_frame(int frame);
virtual bool do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
int z, int n, int primary_file_num_channels, int alpha_file_channel);
virtual bool do_load_one(const PNMImage &pnmimage, int z, int n);
private:
class VideoPage;
class VideoStream;

View File

@ -36,9 +36,10 @@ 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 = 2;
static const unsigned short _bam_minor_ver = 3;
// Bumped to minor version 1 on 3/12/06 to add Texture::_compression.
// Bumped to minor version 2 on 3/17/06 to add PandaNode::_draw_control_mask.
// Bumped to minor version 3 on 3/21/06 to add Texture::_ram_images.
#endif

View File

@ -118,7 +118,7 @@ EggToBam() :
"stored uncompressed. A particular texture that is encoded into "
"multiple different bam files in this way cannot be unified into "
"the same part of texture memory if the different bam files are loaded "
"together. All that being said, this can sometimes be a convenient "
"together. That being said, this can sometimes be a convenient "
"way to ensure the bam file is completely self-contained.",
&EggToBam::dispatch_none, &_tex_rawdata);
@ -162,6 +162,16 @@ EggToBam() :
"on the particular graphics card that was used to generate them.",
&EggToBam::dispatch_none, &_tex_ctex);
add_option
("mipmap", "", 0,
"Records the pre-generated mipmap levels in the texture object file "
"when using -rawtex or -txo, for textures that use mipmapping. This "
"will increase the size of the texture object file by about 33%, but "
"it prevents the need to compute the mipmaps at runtime. The default "
"is to record mipmap levels only when the texture was specifically "
"loaded with them.",
&EggToBam::dispatch_none, &_tex_mipmap);
add_option
("load-display", "display name", 0,
"Specifies the particular display module to load to perform the texture "
@ -241,10 +251,20 @@ run() {
if (_tex_ctex) {
tex->set_keep_ram_image(true);
tex->set_compression(Texture::CM_on);
bool has_mipmap_levels = (tex->get_num_ram_mipmap_images() > 1);
if (!_engine->extract_texture_data(tex, _gsg)) {
nout << " couldn't compress " << tex->get_name() << "\n";
}
if (!has_mipmap_levels && !_tex_mipmap) {
// Make sure we didn't accidentally introduce mipmap levels
// by rendezvousing through the graphics card.
tex->clear_ram_mipmap_images();
}
} else if (_tex_mipmap && tex->uses_mipmaps()) {
// Generate mipmap levels.
tex->generate_ram_mipmap_images();
}
if (_tex_txo || _tex_txopz) {
convert_txo(tex);
}

View File

@ -69,6 +69,7 @@ private:
bool _tex_txo;
bool _tex_txopz;
bool _tex_ctex;
bool _tex_mipmap;
string _load_display;
// The rest of this is required to support -ctex.