Merge changes from the Witness.

This commit is contained in:
castano@gmail.com
2013-06-07 17:53:55 +00:00
parent 634229a842
commit 94d0635285
49 changed files with 1974 additions and 625 deletions

View File

@ -22,7 +22,7 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#include "CompressorDXT.h"
#include "BlockCompressor.h"
#include "OutputOptions.h"
#include "TaskDispatcher.h"
@ -46,7 +46,7 @@ using namespace nvtt;
#include <omp.h>
#endif
void FixedBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, const float * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
void ColorBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, const float * data, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
{
const uint bs = blockSize();
const uint bw = (w + 3) / 4;
@ -113,7 +113,7 @@ void FixedBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, c
*/
struct FixedBlockCompressorContext
struct ColorBlockCompressorContext
{
nvtt::AlphaMode alphaMode;
uint w, h;
@ -122,13 +122,13 @@ struct FixedBlockCompressorContext
uint bw, bh, bs;
uint8 * mem;
FixedBlockCompressor * compressor;
ColorBlockCompressor * compressor;
};
// Each task compresses one block.
void FixedBlockCompressorTask(void * data, int i)
void ColorBlockCompressorTask(void * data, int i)
{
FixedBlockCompressorContext * d = (FixedBlockCompressorContext *) data;
ColorBlockCompressorContext * d = (ColorBlockCompressorContext *) data;
uint x = i % d->bw;
uint y = i / d->bw;
@ -143,11 +143,11 @@ void FixedBlockCompressorTask(void * data, int i)
}
}
void FixedBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, uint d, const float * data, nvtt::TaskDispatcher * dispatcher, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
void ColorBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, uint d, const float * data, nvtt::TaskDispatcher * dispatcher, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions)
{
nvDebugCheck(d == 1);
FixedBlockCompressorContext context;
ColorBlockCompressorContext context;
context.alphaMode = alphaMode;
context.w = w;
context.h = h;
@ -169,7 +169,7 @@ void FixedBlockCompressor::compress(nvtt::AlphaMode alphaMode, uint w, uint h, u
const uint size = context.bs * count;
context.mem = new uint8[size];
dispatcher->dispatch(FixedBlockCompressorTask, &context, count);
dispatcher->dispatch(ColorBlockCompressorTask, &context, count);
outputOptions.writeData(context.mem, size);

View File

@ -22,8 +22,8 @@
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#ifndef NVTT_COMPRESSORDXT_H
#define NVTT_COMPRESSORDXT_H
#ifndef NVTT_BLOCKCOMPRESSOR_H
#define NVTT_BLOCKCOMPRESSOR_H
#include "Compressor.h"
@ -33,7 +33,7 @@ namespace nv
struct ColorSet;
struct ColorBlock;
struct FixedBlockCompressor : public CompressorInterface
struct ColorBlockCompressor : public CompressorInterface
{
virtual void compress(nvtt::AlphaMode alphaMode, uint w, uint h, uint d, const float * rgba, nvtt::TaskDispatcher * dispatcher, const nvtt::CompressionOptions::Private & compressionOptions, const nvtt::OutputOptions::Private & outputOptions);
@ -52,4 +52,4 @@ namespace nv
} // nv namespace
#endif // NVTT_COMPRESSORDXT_H
#endif // NVTT_BLOCKCOMPRESSOR_H

View File

@ -25,20 +25,20 @@
#ifndef NVTT_COMPRESSORDX10_H
#define NVTT_COMPRESSORDX10_H
#include "CompressorDXT.h"
#include "BlockCompressor.h"
namespace nv
{
struct ColorBlock;
// Fast CPU compressors.
struct FastCompressorBC4 : public FixedBlockCompressor
struct FastCompressorBC4 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }
};
struct FastCompressorBC5 : public FixedBlockCompressor
struct FastCompressorBC5 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }
@ -46,13 +46,13 @@ namespace nv
// Production CPU compressors.
struct ProductionCompressorBC4 : public FixedBlockCompressor
struct ProductionCompressorBC4 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }
};
struct ProductionCompressorBC5 : public FixedBlockCompressor
struct ProductionCompressorBC5 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }

View File

@ -24,7 +24,7 @@
#ifndef NVTT_COMPRESSORDX11_H
#define NVTT_COMPRESSORDX11_H
#include "CompressorDXT.h"
#include "BlockCompressor.h"
namespace nv
{

View File

@ -122,11 +122,7 @@ void CompressorDXT1::compressBlock(ColorSet & set, nvtt::AlphaMode alphaMode, co
if (set.isSingleColor(/*ignoreAlpha*/true))
{
Color32 c;
c.r = uint8(clamp(set.colors[0].x, 0.0f, 1.0f) * 255);
c.g = uint8(clamp(set.colors[0].y, 0.0f, 1.0f) * 255);
c.b = uint8(clamp(set.colors[0].z, 0.0f, 1.0f) * 255);
c.a = 255;
Color32 c = toColor32(set.colors[0]);
OptimalCompress::compressDXT1(c, block);
}
else
@ -202,7 +198,6 @@ void CompressorDXT1_Luma::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alpha
OptimalCompress::compressDXT1_Luma(rgba, block);
}
void CompressorDXT3::compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output)
{
BlockDXT3 * block = new(output) BlockDXT3;

View File

@ -25,38 +25,38 @@
#ifndef NVTT_COMPRESSORDX9_H
#define NVTT_COMPRESSORDX9_H
#include "CompressorDXT.h"
#include "BlockCompressor.h"
namespace nv
{
struct ColorBlock;
// Fast CPU compressors.
struct FastCompressorDXT1 : public FixedBlockCompressor
struct FastCompressorDXT1 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }
};
struct FastCompressorDXT1a : public FixedBlockCompressor
struct FastCompressorDXT1a : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }
};
struct FastCompressorDXT3 : public FixedBlockCompressor
struct FastCompressorDXT3 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }
};
struct FastCompressorDXT5 : public FixedBlockCompressor
struct FastCompressorDXT5 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }
};
struct FastCompressorDXT5n : public FixedBlockCompressor
struct FastCompressorDXT5n : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }
@ -71,38 +71,38 @@ namespace nv
virtual uint blockSize() const { return 8; }
};
#else
struct CompressorDXT1 : public FixedBlockCompressor
struct CompressorDXT1 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }
};
#endif
struct CompressorDXT1a : public FixedBlockCompressor
struct CompressorDXT1a : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }
};
struct CompressorDXT1_Luma : public FixedBlockCompressor
struct CompressorDXT1_Luma : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }
};
struct CompressorDXT3 : public FixedBlockCompressor
struct CompressorDXT3 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }
};
struct CompressorDXT5 : public FixedBlockCompressor
struct CompressorDXT5 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }
};
struct CompressorDXT5n : public FixedBlockCompressor
struct CompressorDXT5n : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 16; }
@ -137,7 +137,7 @@ namespace nv
#endif
#if defined(HAVE_STB)
struct StbCompressorDXT1 : public FixedBlockCompressor
struct StbCompressorDXT1 : public ColorBlockCompressor
{
virtual void compressBlock(ColorBlock & rgba, nvtt::AlphaMode alphaMode, const nvtt::CompressionOptions::Private & compressionOptions, void * output);
virtual uint blockSize() const { return 8; }

View File

@ -349,20 +349,23 @@ void PixelFormatConverter::compress(nvtt::AlphaMode /*alphaMode*/, uint w, uint
}
else
{
Color32 c;
if (compressionOptions.pixelType == nvtt::PixelType_UnsignedNorm) {
c.r = uint8(clamp(r * 255, 0.0f, 255.0f));
c.g = uint8(clamp(g * 255, 0.0f, 255.0f));
c.b = uint8(clamp(b * 255, 0.0f, 255.0f));
c.a = uint8(clamp(a * 255, 0.0f, 255.0f));
}
// We first convert to 16 bits, then to the target size. @@ If greater than 16 bits, this will truncate and bitexpand.
// @@ Add support for nvtt::PixelType_SignedInt, nvtt::PixelType_SignedNorm, nvtt::PixelType_UnsignedInt
int ir, ig, ib, ia;
if (compressionOptions.pixelType == nvtt::PixelType_UnsignedNorm) {
ir = iround(clamp(r * 65535.0f, 0.0f, 65535.0f));
ig = iround(clamp(g * 65535.0f, 0.0f, 65535.0f));
ib = iround(clamp(b * 65535.0f, 0.0f, 65535.0f));
ia = iround(clamp(a * 65535.0f, 0.0f, 65535.0f));
}
uint p = 0;
p |= PixelFormat::convert(c.r, 8, rsize) << rshift;
p |= PixelFormat::convert(c.g, 8, gsize) << gshift;
p |= PixelFormat::convert(c.b, 8, bsize) << bshift;
p |= PixelFormat::convert(c.a, 8, asize) << ashift;
p |= PixelFormat::convert(ir, 16, rsize) << rshift;
p |= PixelFormat::convert(ig, 16, gsize) << gshift;
p |= PixelFormat::convert(ib, 16, bsize) << bshift;
p |= PixelFormat::convert(ia, 16, asize) << ashift;
stream.putBits(p, bitCount);
}

View File

@ -429,6 +429,12 @@ void CubeSurface::range(int channel, float * minimum_ptr, float * maximum_ptr) c
*maximum_ptr = maximum;
}
void CubeSurface::clamp(int channel, float low/*= 0.0f*/, float high/*= 1.0f*/) {
for (int f = 0; f < 6; f++) {
m->face[f].clamp(channel, low, high);
}
}
#include "nvmath/SphericalHarmonic.h"
@ -470,13 +476,114 @@ CubeSurface CubeSurface::irradianceFilter(int size, EdgeFixup fixupMethod) const
}
// Warp uv coordinate from [-1, 1] to
/*float warp(float u, int size) {
}*/
// Convolve filter against this cube.
Vector3 CubeSurface::Private::applyAngularFilter(const Vector3 & filterDir, float coneAngle, float * filterTable, int tableSize)
{
const float cosineConeAngle = cos(coneAngle);
nvDebugCheck(cosineConeAngle >= 0);
Vector3 color(0);
float sum = 0;
// Things I have tried to speed this up:
// - Compute accurate bounds assuming cone axis aligned to plane, result was too small elsewhere.
// - Compute ellipse that results in the cone/plane intersection and compute its bounds. Sometimes intersection is a parabolla, hard to handle that case.
// - Compute the 6 axis aligned planes that bound the cone, clip faces against planes. Resulting plane equations are way too complex.
// What AMD CubeMapGen does:
// - Compute conservative bounds on the primary face, wrap around the adjacent faces.
// For each texel of the input cube.
for (uint f = 0; f < 6; f++) {
// Test face cone agains filter cone.
float cosineFaceAngle = dot(filterDir, faceNormals[f]);
float faceAngle = acosf(cosineFaceAngle);
if (faceAngle > coneAngle + atanf(sqrtf(2))) {
// Skip face.
continue;
}
const int L = toI32(edgeLength-1);
int x0 = 0, x1 = L;
int y0 = 0, y1 = L;
#if 0
float u0 = -1;
float u1 = 1;
float v0 = -1;
float v1 = 1;
// @@ Compute uvs.
// Expand uv coordinates from [-1,1] to [0, edgeLength)
u0 = (u0 + 1) * edgeLength * 0.5f - 0.5f;
v0 = (v0 + 1) * edgeLength * 0.5f - 0.5f;
u1 = (u1 + 1) * edgeLength * 0.5f - 0.5f;
v1 = (v1 + 1) * edgeLength * 0.5f - 0.5f;
nvDebugCheck(u0 >= -0.5f && u0 <= edgeLength - 0.5f);
nvDebugCheck(v0 >= -0.5f && v0 <= edgeLength - 0.5f);
nvDebugCheck(u1 >= -0.5f && u1 <= edgeLength - 0.5f);
nvDebugCheck(v1 >= -0.5f && v1 <= edgeLength - 0.5f);
x0 = clamp(ifloor(u0), 0, L);
y0 = clamp(ifloor(v0), 0, L);
x1 = clamp(iceil(u1), 0, L);
y1 = clamp(iceil(v1), 0, L);
#endif
nvDebugCheck(x1 >= x0);
nvDebugCheck(y1 >= y0);
if (x1 == x0 || y1 == y0) {
// Skip this face.
continue;
}
const Surface & inputFace = face[f];
const FloatImage * inputImage = inputFace.m->image;
for (int y = y0; y <= y1; y++) {
bool inside = false;
for (int x = x0; x <= x1; x++) {
Vector3 dir = texelTable->direction(f, x, y);
float cosineAngle = dot(dir, filterDir);
if (cosineAngle > cosineConeAngle) {
float solidAngle = texelTable->solidAngle(f, x, y);
//float scale = powf(saturate(cosineAngle), cosinePower);
int idx = int(saturate(cosineAngle) * (tableSize - 1));
float scale = filterTable[idx]; // @@ Do bilinear interpolation?
float contribution = solidAngle * scale;
sum += contribution;
color.x += contribution * inputImage->pixel(0, x, y, 0);
color.y += contribution * inputImage->pixel(1, x, y, 0);
color.z += contribution * inputImage->pixel(2, x, y, 0);
inside = true;
}
else if (inside) {
// Filter scale is monotonic, if we have been inside once and we just exit, then we can skip the rest of the row.
// We could do the same thing for the columns and skip entire rows.
break;
}
}
}
}
color *= (1.0f / sum);
return color;
}
// We want to find the alpha such that:
// cos(alpha)^cosinePower = epsilon
@ -491,6 +598,7 @@ CubeSurface CubeSurface::irradianceFilter(int size, EdgeFixup fixupMethod) const
// - parallelize. Done.
// - use ISPC?
// Convolve filter against this cube.
Vector3 CubeSurface::Private::applyCosinePowerFilter(const Vector3 & filterDir, float coneAngle, float cosinePower)
{
@ -500,6 +608,15 @@ Vector3 CubeSurface::Private::applyCosinePowerFilter(const Vector3 & filterDir,
Vector3 color(0);
float sum = 0;
// Things I have tried to speed this up:
// - Compute accurate bounds assuming cone axis aligned to plane, result was too small elsewhere.
// - Compute ellipse that results in the cone/plane intersection and compute its bounds. Sometimes intersection is a parabolla, hard to handle that case.
// - Compute the 6 axis aligned planes that bound the cone, clip faces against planes. Resulting plane equations are way too complex.
// What AMD CubeMapGen does:
// - Compute conservative bounds on the primary face, wrap around the adjacent faces.
// For each texel of the input cube.
for (uint f = 0; f < 6; f++) {
@ -512,163 +629,36 @@ Vector3 CubeSurface::Private::applyCosinePowerFilter(const Vector3 & filterDir,
continue;
}
// @@ We could do a less conservative test and test the face frustum against the cone...
// Or maybe easier: the face quad against the cone.
// Compute bounding box of cone intersection against face.
// The intersection of the cone with the face is an elipse, we want the extents of that elipse.
// @@ Hmm... we could even rasterize an elipse! Sounds like FUN!
const int L = toI32(edgeLength-1);
int x0 = 0, x1 = L;
int y0 = 0, y1 = L;
if (false) {
// New approach?
#if 0
float u0 = -1;
float u1 = 1;
float v0 = -1;
float v1 = 1;
// For each face, we are looking for 4 planes that bound the cone.
// @@ Compute uvs.
// All planes go through the origin.
// Plane fully determined by its normal.
// We only care about planes aligned to one axis. So, for the XY face, we have 4 planes:
// Expand uv coordinates from [-1,1] to [0, edgeLength)
u0 = (u0 + 1) * edgeLength * 0.5f - 0.5f;
v0 = (v0 + 1) * edgeLength * 0.5f - 0.5f;
u1 = (u1 + 1) * edgeLength * 0.5f - 0.5f;
v1 = (v1 + 1) * edgeLength * 0.5f - 0.5f;
nvDebugCheck(u0 >= -0.5f && u0 <= edgeLength - 0.5f);
nvDebugCheck(v0 >= -0.5f && v0 <= edgeLength - 0.5f);
nvDebugCheck(u1 >= -0.5f && u1 <= edgeLength - 0.5f);
nvDebugCheck(v1 >= -0.5f && v1 <= edgeLength - 0.5f);
// Plane goes through origin.
// Plane normal is unit length.
// Plane must be tangent to cone ->
// angle between plane normal and cone axis is 90 - cone angle & 90 + cone angle
// dot(N, D) == cos(90 - cone angle)
// dot(N, D) == cos(90 + cone angle)
// Plane must contain face UV axis
// Find the 4 planes and how they intersect the unit face, which gives us (u0,v0, u1,v1).
// Expand uv coordinates, clamp to
}
// @@ Ugh. This is wrong, or only right when filterDir is aligned to one axis.
if (false) {
// uv coordinates corresponding to filterDir.
//float u = dot(filterDir, faceU[f]) / cosineFaceAngle;
//float v = dot(filterDir, faceV[f]) / cosineFaceAngle;
// Angular coordinates corresponding to filterDir with respect to faceNormal.
float atu = atan2(dot(filterDir, faceU[f]), cosineFaceAngle);
float atv = atan2(dot(filterDir, faceV[f]), cosineFaceAngle);
// Expand angles and project back to the face plane.
float u0 = tan(clamp(atu - coneAngle, -PI/4, PI/4));
float v0 = tan(clamp(atv - coneAngle, -PI/4, PI/4));
float u1 = tan(clamp(atu + coneAngle, -PI/4, PI/4));
float v1 = tan(clamp(atv + coneAngle, -PI/4, PI/4));
nvDebugCheck(u0 >= -1 && u0 <= 1);
nvDebugCheck(v0 >= -1 && v0 <= 1);
nvDebugCheck(u1 >= -1 && u1 <= 1);
nvDebugCheck(v1 >= -1 && v1 <= 1);
// Expand uv coordinates from [-1,1] to [0, edgeLength)
u0 = (u0 + 1) * edgeLength * 0.5f - 0.5f;
v0 = (v0 + 1) * edgeLength * 0.5f - 0.5f;
u1 = (u1 + 1) * edgeLength * 0.5f - 0.5f;
v1 = (v1 + 1) * edgeLength * 0.5f - 0.5f;
nvDebugCheck(u0 >= -0.5f && u0 <= edgeLength - 0.5f);
nvDebugCheck(v0 >= -0.5f && v0 <= edgeLength - 0.5f);
nvDebugCheck(u1 >= -0.5f && u1 <= edgeLength - 0.5f);
nvDebugCheck(v1 >= -0.5f && v1 <= edgeLength - 0.5f);
x0 = clamp(ifloor(u0), 0, L);
y0 = clamp(ifloor(v0), 0, L);
x1 = clamp(iceil(u1), 0, L);
y1 = clamp(iceil(v1), 0, L);
nvDebugCheck(x1 >= x0);
nvDebugCheck(y1 >= y0);
}
// This is elegant and all that, but the problem is that the projection is not always an ellipse, but often a parabola.
// A parabola has infinite bounds, so this approach is not very practical. Ugh.
if (false) {
//nvCheck(cosineFaceAngle >= 0.0f); @@ Not true for wide angles.
// Focal point in cartessian coordinates:
Vector3 F = Vector3(dot(faceU[f], filterDir), dot(faceV[f], filterDir), cosineFaceAngle);
// Focal point in polar coordinates:
Vector2 Fp = toPolar(F);
nvCheck(Fp.y >= 0.0f); // top
//nvCheck(Fp.y <= PI/2); // horizon
// If this is an ellipse:
if (Fp.y + coneAngle < PI/2) {
nvCheck(Fp.y - coneAngle > -PI/2);
// Major axis endpoints:
Vector2 Fa1 = toPlane(Fp.x, Fp.y - cosineFaceAngle); // near endpoint.
Vector2 Fa2 = toPlane(Fp.x, Fp.y + cosineFaceAngle); // far endpoint.
nvCheck(length(Fa1) <= length(Fa2));
// Ellipse center:
Vector2 Fc = (Fa1 + Fa2) * 0.5f;
// Major radius:
float a = 0.5f * length(Fa1 - Fa2);
// Focal point:
Vector2 F1 = toPlane(Fp.x, Fp.y);
// If we project Fa1, Fa2, Fc, F1 onto the filter direction, then:
float da1 = dot(Fa1, F.xy()) / fabs(cosineFaceAngle);
float d1 = dot(F1, F.xy()) / fabs(cosineFaceAngle);
float dc = dot(Fc, F.xy()) / fabs(cosineFaceAngle);
float da2 = dot(Fa2, F.xy()) / fabs(cosineFaceAngle);
//nvDebug("%f <= %f <= %f <= %f (%d: %f %f | %f %f)\n", da1, d1, dc, da2, f, F.x, F.y, Fp.y - coneAngle, Fp.y + coneAngle);
//nvCheck(da1 <= d1 && d1 <= dc && dc <= da2);
// Translate focal point relative to center:
F1 -= Fc;
// Focal distance:
//float f = length(F1); // @@ Overriding f!
// Minor radius:
//float b = sqrtf(a*a - f*f);
// Second order quadric coefficients:
float A = a*a - F1.x * F1.x;
nvCheck(A >= 0);
float B = a*a - F1.y * F1.y;
nvCheck(B >= 0);
// Floating point bounds:
float u0 = clamp(Fc.x - sqrtf(B), -1.0f, 1.0f);
float u1 = clamp(Fc.x + sqrtf(B), -1.0f, 1.0f);
float v0 = clamp(Fc.y - sqrtf(A), -1.0f, 1.0f);
float v1 = clamp(Fc.y + sqrtf(A), -1.0f, 1.0f);
// Expand uv coordinates from [-1,1] to [0, edgeLength)
u0 = (u0 + 1) * edgeLength * 0.5f - 0.5f;
v0 = (v0 + 1) * edgeLength * 0.5f - 0.5f;
u1 = (u1 + 1) * edgeLength * 0.5f - 0.5f;
v1 = (v1 + 1) * edgeLength * 0.5f - 0.5f;
//nvDebugCheck(u0 >= -0.5f && u0 <= edgeLength - 0.5f);
//nvDebugCheck(v0 >= -0.5f && v0 <= edgeLength - 0.5f);
//nvDebugCheck(u1 >= -0.5f && u1 <= edgeLength - 0.5f);
//nvDebugCheck(v1 >= -0.5f && v1 <= edgeLength - 0.5f);
x0 = clamp(ifloor(u0), 0, L);
y0 = clamp(ifloor(v0), 0, L);
x1 = clamp(iceil(u1), 0, L);
y1 = clamp(iceil(v1), 0, L);
nvDebugCheck(x1 >= x0);
nvDebugCheck(y1 >= y0);
}
// @@ What to do with parabolas?
}
x0 = clamp(ifloor(u0), 0, L);
y0 = clamp(ifloor(v0), 0, L);
x1 = clamp(iceil(u1), 0, L);
y1 = clamp(iceil(v1), 0, L);
#endif
nvDebugCheck(x1 >= x0);
nvDebugCheck(y1 >= y0);
if (x1 == x0 || y1 == y0) {
// Skip this face.
@ -714,17 +704,18 @@ Vector3 CubeSurface::Private::applyCosinePowerFilter(const Vector3 & filterDir,
#include "nvthread/ParallelFor.h"
struct ApplyCosinePowerFilterContext {
struct ApplyAngularFilterContext {
CubeSurface::Private * inputCube;
CubeSurface::Private * filteredCube;
float coneAngle;
float cosinePower;
float * filterTable;
int tableSize;
EdgeFixup fixupMethod;
};
void ApplyCosinePowerFilterTask(void * context, int id)
void ApplyAngularFilterTask(void * context, int id)
{
ApplyCosinePowerFilterContext * ctx = (ApplyCosinePowerFilterContext *)context;
ApplyAngularFilterContext * ctx = (ApplyAngularFilterContext *)context;
int size = ctx->filteredCube->edgeLength;
@ -739,7 +730,7 @@ void ApplyCosinePowerFilterTask(void * context, int id)
const Vector3 filterDir = texelDirection(f, x, y, size, ctx->fixupMethod);
// Convolve filter against cube.
Vector3 color = ctx->inputCube->applyCosinePowerFilter(filterDir, ctx->coneAngle, ctx->cosinePower);
Vector3 color = ctx->inputCube->applyAngularFilter(filterDir, ctx->coneAngle, ctx->filterTable, ctx->tableSize);
filteredImage->pixel(0, idx) = color.x;
filteredImage->pixel(1, idx) = color.y;
@ -749,8 +740,6 @@ void ApplyCosinePowerFilterTask(void * context, int id)
CubeSurface CubeSurface::cosinePowerFilter(int size, float cosinePower, EdgeFixup fixupMethod) const
{
const uint edgeLength = m->edgeLength;
// Allocate output cube.
CubeSurface filteredCube;
filteredCube.m->allocate(size);
@ -782,14 +771,24 @@ CubeSurface CubeSurface::cosinePowerFilter(int size, float cosinePower, EdgeFixu
}
}*/
ApplyCosinePowerFilterContext context;
ApplyAngularFilterContext context;
context.inputCube = m;
context.filteredCube = filteredCube.m;
context.coneAngle = coneAngle;
context.cosinePower = cosinePower;
context.fixupMethod = fixupMethod;
nv::ParallelFor parallelFor(ApplyCosinePowerFilterTask, &context);
context.tableSize = 512;
context.filterTable = new float[context.tableSize];
// @@ Instead of looking up table between [0 - 1] we should probably use [cos(coneAngle), 1]
for (int i = 0; i < context.tableSize; i++) {
float f = float(i) / (context.tableSize - 1);
context.filterTable[i] = powf(f, cosinePower);
}
nv::ParallelFor parallelFor(ApplyAngularFilterTask, &context);
parallelFor.run(6 * size * size);
// @@ Implement edge averaging.
@ -816,6 +815,72 @@ CubeSurface CubeSurface::cosinePowerFilter(int size, float cosinePower, EdgeFixu
}
// Sample cubemap in the given direction.
Vector3 CubeSurface::Private::sample(const Vector3 & dir)
{
int f = -1;
if (fabs(dir.x) > fabs(dir.y) && fabs(dir.x) > fabs(dir.z)) {
if (dir.x > 0) f = 0;
else f = 1;
}
else if (fabs(dir.y) > fabs(dir.z)) {
if (dir.y > 0) f = 2;
else f = 3;
}
else {
if (dir.z > 0) f = 4;
else f = 5;
}
nvDebugCheck(f != -1);
// uv coordinates corresponding to filterDir.
float u = dot(dir, faceU[f]);
float v = dot(dir, faceV[f]);
FloatImage * img = face[f].m->image;
Vector3 color;
color.x = img->sampleLinearClamp(0, u, v);
color.y = img->sampleLinearClamp(1, u, v);
color.z = img->sampleLinearClamp(2, u, v);
return color;
}
// @@ Not tested!
CubeSurface CubeSurface::fastResample(int size, EdgeFixup fixupMethod) const
{
// Allocate output cube.
CubeSurface resampledCube;
resampledCube.m->allocate(size);
// For each texel of the output cube.
for (uint f = 0; f < 6; f++) {
nvtt::Surface resampledFace = resampledCube.m->face[f];
FloatImage * resampledImage = resampledFace.m->image;
for (uint y = 0; y < uint(size); y++) {
for (uint x = 0; x < uint(size); x++) {
const Vector3 filterDir = texelDirection(f, x, y, size, fixupMethod);
Vector3 color = m->sample(filterDir);
resampledImage->pixel(0, x, y, 0) = color.x;
resampledImage->pixel(1, x, y, 0) = color.y;
resampledImage->pixel(2, x, y, 0) = color.z;
}
}
}
// @@ Implement edge averaging. Share this code with cosinePowerFilter
if (fixupMethod == EdgeFixup_Average) {
}
return resampledCube;
}
void CubeSurface::toLinear(float gamma)
{
if (isNull()) return;

View File

@ -94,8 +94,11 @@ namespace nvtt
}
// Filtering helpers:
nv::Vector3 applyAngularFilter(const nv::Vector3 & dir, float coneAngle, float * filterTable, int tableSize);
nv::Vector3 applyCosinePowerFilter(const nv::Vector3 & dir, float coneAngle, float cosinePower);
nv::Vector3 sample(const nv::Vector3 & dir);
uint edgeLength;
Surface face[6];
TexelTable * texelTable;

View File

@ -1,82 +1,82 @@
// Copyright (c) 2009-2011 Ignacio Castano <castano@gmail.com>
// Copyright (c) 2007-2009 NVIDIA Corporation -- Ignacio Castano <icastano@nvidia.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#ifndef NVTT_INPUTOPTIONS_H
#define NVTT_INPUTOPTIONS_H
#include "nvtt.h"
#include "nvmath/Vector.h"
namespace nvtt
{
struct InputOptions::Private
{
Private() : images(NULL) {}
WrapMode wrapMode;
TextureType textureType;
InputFormat inputFormat;
AlphaMode alphaMode;
uint width;
uint height;
uint depth;
uint faceCount;
uint mipmapCount;
uint imageCount;
void ** images;
// Gamma conversion.
float inputGamma;
float outputGamma;
// Mipmap generation options.
bool generateMipmaps;
int maxLevel;
MipmapFilter mipmapFilter;
// Kaiser filter parameters.
float kaiserWidth;
float kaiserAlpha;
float kaiserStretch;
// Normal map options.
bool isNormalMap;
bool normalizeMipmaps;
bool convertToNormalMap;
nv::Vector4 heightFactors;
nv::Vector4 bumpFrequencyScale;
// Adjust extents.
uint maxExtent;
RoundMode roundMode;
};
} // nvtt namespace
#endif // NVTT_INPUTOPTIONS_H
// Copyright (c) 2009-2011 Ignacio Castano <castano@gmail.com>
// Copyright (c) 2007-2009 NVIDIA Corporation -- Ignacio Castano <icastano@nvidia.com>
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#ifndef NVTT_INPUTOPTIONS_H
#define NVTT_INPUTOPTIONS_H
#include "nvtt.h"
#include "nvmath/Vector.h"
namespace nvtt
{
struct InputOptions::Private
{
Private() : images(NULL) {}
WrapMode wrapMode;
TextureType textureType;
InputFormat inputFormat;
AlphaMode alphaMode;
uint width;
uint height;
uint depth;
uint faceCount;
uint mipmapCount;
uint imageCount;
void ** images;
// Gamma conversion.
float inputGamma;
float outputGamma;
// Mipmap generation options.
bool generateMipmaps;
int maxLevel;
MipmapFilter mipmapFilter;
// Kaiser filter parameters.
float kaiserWidth;
float kaiserAlpha;
float kaiserStretch;
// Normal map options.
bool isNormalMap;
bool normalizeMipmaps;
bool convertToNormalMap;
nv::Vector4 heightFactors;
nv::Vector4 bumpFrequencyScale;
// Adjust extents.
uint maxExtent;
RoundMode roundMode;
};
} // nvtt namespace
#endif // NVTT_INPUTOPTIONS_H

View File

@ -40,6 +40,10 @@
#include <float.h>
#include <string.h> // memset, memcpy
#if NV_CC_GNUC
#include <math.h> // exp2f and log2f
#endif
using namespace nv;
using namespace nvtt;
@ -101,6 +105,20 @@ namespace
}*/
}
bool nv::canMakeNextMipmap(uint w, uint h, uint d, uint min_size)
{
if (min_size==1u) {
if(w==1u && h==1u && d==1u) {
return false;
}
}
else if (((w <= min_size || h <= min_size) && d == 1u)) {
return false;
}
return true;
}
uint nv::countMipmaps(uint w)
{
uint mipmap = 0;
@ -127,6 +145,21 @@ uint nv::countMipmaps(uint w, uint h, uint d)
return mipmap + 1;
}
uint nv::countMipmapsWithMinSize(uint w, uint h, uint d, uint min_size)
{
uint mipmap = 0;
while (canMakeNextMipmap(w, h, d, min_size)) {
w = max(1U, w / 2);
h = max(1U, h / 2);
d = max(1U, d / 2);
mipmap++;
}
return mipmap + 1;
}
uint nv::computeImageSize(uint w, uint h, uint d, uint bitCount, uint pitchAlignmentInBytes, Format format)
{
if (format == Format_RGBA) {
@ -308,10 +341,18 @@ int Surface::countMipmaps() const
return ::countMipmaps(m->image->width(), m->image->height(), 1);
}
int Surface::countMipmaps(int min_size) const
{
if (m->image == NULL) return 0;
return ::countMipmapsWithMinSize(m->image->width(), m->image->height(), 1, min_size);
}
float Surface::alphaTestCoverage(float alphaRef/*= 0.5*/) const
{
if (m->image == NULL) return 0.0f;
alphaRef = nv::clamp(alphaRef, 1.0f/256, 255.0f/256);
return m->image->alphaTestCoverage(alphaRef, 3);
}
@ -348,7 +389,7 @@ float Surface::average(int channel, int alpha_channel/*= -1*/, float gamma /*= 2
// Avoid division by zero.
if (denom == 0.0f) return 0.0f;
return sum / denom;
return powf(sum / denom, 1.0f/gamma);
}
const float * Surface::data() const
@ -356,6 +397,13 @@ const float * Surface::data() const
return m->image->channel(0);
}
const float * Surface::channel(int i) const
{
if (i < 0 || i > 3) return NULL;
return m->image->channel(i);
}
void Surface::histogram(int channel, float rangeMin, float rangeMax, int binCount, int * binPtr) const
{
// We assume it's clear in case we want to accumulate multiple histograms.
@ -378,12 +426,14 @@ void Surface::histogram(int channel, float rangeMin, float rangeMax, int binCoun
}
}
void Surface::range(int channel, float * rangeMin, float * rangeMax) const
void Surface::range(int channel, float * rangeMin, float * rangeMax, int alpha_channel/*= -1*/, float alpha_ref/*= 0.f*/) const
{
Vector2 range(FLT_MAX, -FLT_MAX);
FloatImage * img = m->image;
if (alpha_channel == -1) { // no alpha channel; just like the original range function
if (m->image != NULL)
{
float * c = img->channel(channel);
@ -395,6 +445,25 @@ void Surface::range(int channel, float * rangeMin, float * rangeMax) const
if (f > range.y) range.y = f;
}
}
}
else { // use alpha test to ignore some pixels
//note, it's quite possible to get FLT_MAX,-FLT_MAX back if all pixels fail the test
if (m->image != NULL)
{
const float * c = img->channel(channel);
const float * a = img->channel(alpha_channel);
const uint count = img->pixelCount();
for (uint p = 0; p < count; p++) {
if(a[p]>alpha_ref) {
float f = c[p];
if (f < range.x) range.x = f;
if (f > range.y) range.y = f;
}
}
}
}
*rangeMin = range.x;
*rangeMax = range.y;
@ -423,16 +492,44 @@ bool Surface::load(const char * fileName, bool * hasAlpha/*= NULL*/)
return true;
}
bool Surface::save(const char * fileName) const
bool Surface::save(const char * fileName, bool hasAlpha/*=0*/, bool hdr/*=0*/) const
{
if (m->image != NULL)
{
return ImageIO::saveFloat(fileName, m->image, 0, 4);
if (m->image == NULL) {
return false;
}
return false;
if (hdr) {
return ImageIO::saveFloat(fileName, m->image, 0, 4);
}
else {
AutoPtr<Image> image(m->image->createImage(0, 4));
nvCheck(image != NULL);
if (hasAlpha) {
image->setFormat(Image::Format_ARGB);
}
return ImageIO::save(fileName, image.ptr());
}
}
bool Surface::setImage(int w, int h, int d)
{
detach();
if (m->image == NULL) {
m->image = new FloatImage();
}
m->image->allocate(4, w, h, d);
m->type = (d == 1) ? TextureType_2D : TextureType_3D;
m->image->clear();
return true;
}
#if 0 //NV_OS_WIN32
#include <windows.h>
@ -449,13 +546,14 @@ static int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep) {
}
#define TRY __try
#define CATCH __except (filter(GetExceptionCode(), GetExceptionInformation()))
#else
#else // 0
#define TRY if (true)
#define CATCH else
#endif
#endif
bool Surface::setImage(nvtt::InputFormat format, int w, int h, int d, const void * data)
{
@ -553,13 +651,13 @@ bool Surface::setImage(InputFormat format, int w, int h, int d, const void * r,
const uint8 * bsrc = (const uint8 *)b;
const uint8 * asrc = (const uint8 *)a;
try {
TRY {
for (int i = 0; i < count; i++) rdst[i] = float(rsrc[i]) / 255.0f;
for (int i = 0; i < count; i++) gdst[i] = float(gsrc[i]) / 255.0f;
for (int i = 0; i < count; i++) bdst[i] = float(bsrc[i]) / 255.0f;
for (int i = 0; i < count; i++) adst[i] = float(asrc[i]) / 255.0f;
}
catch(...) {
CATCH {
return false;
}
}
@ -570,13 +668,13 @@ bool Surface::setImage(InputFormat format, int w, int h, int d, const void * r,
const uint16 * bsrc = (const uint16 *)b;
const uint16 * asrc = (const uint16 *)a;
try {
TRY {
for (int i = 0; i < count; i++) ((uint32 *)rdst)[i] = half_to_float(rsrc[i]);
for (int i = 0; i < count; i++) ((uint32 *)gdst)[i] = half_to_float(gsrc[i]);
for (int i = 0; i < count; i++) ((uint32 *)bdst)[i] = half_to_float(bsrc[i]);
for (int i = 0; i < count; i++) ((uint32 *)adst)[i] = half_to_float(asrc[i]);
}
catch(...) {
CATCH {
return false;
}
}
@ -587,13 +685,13 @@ bool Surface::setImage(InputFormat format, int w, int h, int d, const void * r,
const float * bsrc = (const float *)b;
const float * asrc = (const float *)a;
try {
TRY {
memcpy(rdst, rsrc, count * sizeof(float));
memcpy(gdst, gsrc, count * sizeof(float));
memcpy(bdst, bsrc, count * sizeof(float));
memcpy(adst, asrc, count * sizeof(float));
}
catch(...) {
CATCH {
return false;
}
}
@ -624,87 +722,87 @@ bool Surface::setImage2D(Format format, Decoder decoder, int w, int h, const voi
const uint8 * ptr = (const uint8 *)data;
try {
TRY {
for (int y = 0; y < bh; y++)
{
for (int x = 0; x < bw; x++)
{
ColorBlock colors;
if (format == nvtt::Format_BC1)
{
const BlockDXT1 * block = (const BlockDXT1 *)ptr;
if (format == nvtt::Format_BC1)
{
const BlockDXT1 * block = (const BlockDXT1 *)ptr;
if (decoder == Decoder_D3D10) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_D3D9) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_NV5x) {
block->decodeBlockNV5x(&colors);
}
}
else if (format == nvtt::Format_BC2)
{
const BlockDXT3 * block = (const BlockDXT3 *)ptr;
if (decoder == Decoder_D3D10) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_D3D9) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_NV5x) {
block->decodeBlockNV5x(&colors);
}
}
else if (format == nvtt::Format_BC2)
{
const BlockDXT3 * block = (const BlockDXT3 *)ptr;
if (decoder == Decoder_D3D10) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_D3D9) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_NV5x) {
block->decodeBlockNV5x(&colors);
}
}
else if (format == nvtt::Format_BC3)
{
const BlockDXT5 * block = (const BlockDXT5 *)ptr;
if (decoder == Decoder_D3D10) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_D3D9) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_NV5x) {
block->decodeBlockNV5x(&colors);
}
}
else if (format == nvtt::Format_BC3)
{
const BlockDXT5 * block = (const BlockDXT5 *)ptr;
if (decoder == Decoder_D3D10) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_D3D9) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_NV5x) {
block->decodeBlockNV5x(&colors);
}
}
else if (format == nvtt::Format_BC4)
{
const BlockATI1 * block = (const BlockATI1 *)ptr;
block->decodeBlock(&colors, decoder == Decoder_D3D9);
if (decoder == Decoder_D3D10) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_D3D9) {
block->decodeBlock(&colors, false);
}
else if (decoder == Decoder_NV5x) {
block->decodeBlockNV5x(&colors);
}
}
else if (format == nvtt::Format_BC4)
{
const BlockATI1 * block = (const BlockATI1 *)ptr;
block->decodeBlock(&colors, decoder == Decoder_D3D9);
}
else if (format == nvtt::Format_BC5)
{
const BlockATI2 * block = (const BlockATI2 *)ptr;
block->decodeBlock(&colors, decoder == Decoder_D3D9);
}
for (int yy = 0; yy < 4; yy++)
{
for (int xx = 0; xx < 4; xx++)
{
Color32 c = colors.color(xx, yy);
if (x * 4 + xx < w && y * 4 + yy < h)
{
m->image->pixel(0, x*4 + xx, y*4 + yy, 0) = float(c.r) * 1.0f/255.0f;
m->image->pixel(1, x*4 + xx, y*4 + yy, 0) = float(c.g) * 1.0f/255.0f;
m->image->pixel(2, x*4 + xx, y*4 + yy, 0) = float(c.b) * 1.0f/255.0f;
m->image->pixel(3, x*4 + xx, y*4 + yy, 0) = float(c.a) * 1.0f/255.0f;
}
}
}
ptr += bs;
}
}
else if (format == nvtt::Format_BC5)
{
const BlockATI2 * block = (const BlockATI2 *)ptr;
block->decodeBlock(&colors, decoder == Decoder_D3D9);
}
for (int yy = 0; yy < 4; yy++)
{
for (int xx = 0; xx < 4; xx++)
{
Color32 c = colors.color(xx, yy);
if (x * 4 + xx < w && y * 4 + yy < h)
{
m->image->pixel(0, x*4 + xx, y*4 + yy, 0) = float(c.r) * 1.0f/255.0f;
m->image->pixel(1, x*4 + xx, y*4 + yy, 0) = float(c.g) * 1.0f/255.0f;
m->image->pixel(2, x*4 + xx, y*4 + yy, 0) = float(c.b) * 1.0f/255.0f;
m->image->pixel(3, x*4 + xx, y*4 + yy, 0) = float(c.a) * 1.0f/255.0f;
}
}
}
ptr += bs;
}
}
}
catch(...) {
CATCH {
return false;
}
@ -812,6 +910,43 @@ void Surface::resize(int w, int h, int d, ResizeFilter filter, float filterWidth
m->image = img;
}
void Surface::resize_make_square(int maxExtent, RoundMode roundMode, ResizeFilter filter)
{
if (isNull()) return;
float filterWidth;
float params[2];
getDefaultFilterWidthAndParams(filter, &filterWidth, params);
int w = m->image->width();
int h = m->image->height();
int d = m->image->depth();
getTargetExtent(&w, &h, &d, maxExtent, roundMode, m->type);
if (m->type == TextureType_2D)
{
nvDebugCheck(d==1);
int md = nv::min(w,h);
w = md;
h = md;
}
else if (m->type == TextureType_Cube)
{
nvDebugCheck(d==1);
nvDebugCheck(w==h);
}
else if (m->type == TextureType_3D)
{
int md = nv::min(nv::min(w,h),d);
w = md;
h = md;
d = md;
}
resize(w, h, d, filter, filterWidth, params);
}
void Surface::resize(int maxExtent, RoundMode roundMode, ResizeFilter filter)
{
float filterWidth;
@ -834,18 +969,26 @@ void Surface::resize(int maxExtent, RoundMode roundMode, ResizeFilter filter, fl
resize(w, h, d, filter, filterWidth, params);
}
bool Surface::buildNextMipmap(MipmapFilter filter)
bool Surface::canMakeNextMipmap(int min_size /*= 1*/)
{
if (isNull()) return false;
return nv::canMakeNextMipmap(width(), height(), depth(), min_size);
}
bool Surface::buildNextMipmap(MipmapFilter filter, int min_size /*= 1*/)
{
float filterWidth;
float params[2];
getDefaultFilterWidthAndParams(filter, &filterWidth, params);
return buildNextMipmap(filter, filterWidth, params);
return buildNextMipmap(filter, filterWidth, params, min_size);
}
bool Surface::buildNextMipmap(MipmapFilter filter, float filterWidth, const float * params)
bool Surface::buildNextMipmap(MipmapFilter filter, float filterWidth, const float * params, int min_size /*= 1*/)
{
if (isNull() || (width() == 1 && height() == 1 && depth() == 1)) {
if (!canMakeNextMipmap(min_size)) {
return false;
}
@ -907,6 +1050,30 @@ bool Surface::buildNextMipmap(MipmapFilter filter, float filterWidth, const floa
return true;
}
bool Surface::buildNextMipmapSolidColor(const float * const color_components)
{
if (isNull() || (width() == 1 && height() == 1 && depth() == 1)) {
return false;
}
detach();
FloatImage * img = new FloatImage();
const uint w = max(1, m->image->m_width / 2);
const uint h = max(1, m->image->m_height / 2);
img->allocate(m->image->m_componentCount, w, h);
for(uint c = 0; c < img->m_componentCount; c++)
{
img->clear(c, color_components[c]);
}
delete m->image;
m->image = img;
return true;
}
void Surface::canvasSize(int w, int h, int d)
{
nvDebugCheck(w > 0 && h > 0 && d > 0);
@ -1083,6 +1250,7 @@ void Surface::transform(const float w0[4], const float w1[4], const float w2[4],
m->image->transform(0, xform, voffset);
}
// R, G, B, A, 1, 0, -1
void Surface::swizzle(int r, int g, int b, int a)
{
if (isNull()) return;
@ -1113,52 +1281,6 @@ void Surface::clamp(int channel, float low, float high)
m->image->clamp(channel, 1, low, high);
}
void Surface::packNormal()
{
if (isNull()) return;
detach();
m->image->scaleBias(0, 3, 0.5f, 0.5f);
}
void Surface::expandNormal()
{
if (isNull()) return;
detach();
m->image->scaleBias(0, 3, 2.0f, -1.0f);
}
// Create a Toksvig map for this normal map.
// http://blog.selfshadow.com/2011/07/22/specular-showdown/
// @@ Assumes this is a normal map expanded in the [-1, 1] range.
Surface Surface::createToksvigMap(float power) const
{
if (isNull()) return Surface();
// @@ TODO
return Surface();
}
// @@ Should I add support for LEAN maps? That requires 5 terms, which would have to be encoded in two textures.
// There's nothing stopping us from having 5 channels in a surface, and then, let the user swizzle them as they wish.
// CLEAN maps are probably more practical, though.
// http://www.cs.umbc.edu/~olano/papers/lean/
// http://gaim.umbc.edu/2011/07/24/shiny-and-clean/
// http://gaim.umbc.edu/2011/07/26/on-error/
NVTT_API Surface Surface::createCleanMap() const
{
if (isNull()) return Surface();
// @@ TODO
return Surface();
}
void Surface::blend(float red, float green, float blue, float alpha, float t)
{
if (isNull()) return;
@ -1285,13 +1407,10 @@ void Surface::fill(float red, float green, float blue, float alpha)
float * a = img->channel(3);
const uint count = img->pixelCount();
for (uint i = 0; i < count; i++)
{
r[i] = red;
g[i] = green;
b[i] = blue;
a[i] = alpha;
}
for (uint i = 0; i < count; i++) r[i] = red;
for (uint i = 0; i < count; i++) g[i] = green;
for (uint i = 0; i < count; i++) b[i] = blue;
for (uint i = 0; i < count; i++) a[i] = alpha;
}
@ -1301,6 +1420,8 @@ void Surface::scaleAlphaToCoverage(float coverage, float alphaRef/*= 0.5f*/)
detach();
alphaRef = nv::clamp(alphaRef, 1.0f/256, 255.0f/256);
m->image->scaleAlphaToCoverage(coverage, alphaRef, 3);
}
@ -1341,7 +1462,6 @@ void Surface::toRGBM(float range/*= 1*/, float threshold/*= 0.25*/)
detach();
threshold = ::clamp(threshold, 1e-6f, 1.0f);
float irange = 1.0f / range;
FloatImage * img = m->image;
float * r = img->channel(0);
@ -1360,6 +1480,7 @@ void Surface::toRGBM(float range/*= 1*/, float threshold/*= 0.25*/)
r[i] = R / M;
g[i] = G / M;
b[i] = B / M;
a[i] = (M - threshold) / (1 - threshold);
#else
@ -1402,6 +1523,7 @@ void Surface::toRGBM(float range/*= 1*/, float threshold/*= 0.25*/)
}
}
void Surface::fromRGBM(float range/*= 1*/)
{
if (isNull()) return;
@ -1425,6 +1547,37 @@ void Surface::fromRGBM(float range/*= 1*/)
}
}
// This is dumb way to encode luminance only values.
void Surface::toLM(float range/*= 1*/, float threshold/*= 0.25*/)
{
if (isNull()) return;
detach();
threshold = ::clamp(threshold, 1e-6f, 1.0f);
FloatImage * img = m->image;
float * r = img->channel(0);
float * g = img->channel(1);
float * b = img->channel(2);
float * a = img->channel(3);
const uint count = img->pixelCount();
for (uint i = 0; i < count; i++) {
float R = nv::clamp(r[i], 0.0f, 1.0f);
float G = nv::clamp(g[i], 0.0f, 1.0f);
float B = nv::clamp(b[i], 0.0f, 1.0f);
float M = max(max(R, G), max(B, threshold));
float L = (R + G + B) / 3;
r[i] = L / M;
b[i] = L / M;
g[i] = L / M;
a[i] = (M - threshold) / (1 - threshold);
}
}
static Color32 toRgbe8(float r, float g, float b)
{
@ -2147,21 +2300,25 @@ void Surface::quantize(int channel, int bits, bool exactEndPoints, bool dither)
FloatImage * img = m->image;
float scale, offset;
float scale, offset0, offset1;
if (exactEndPoints) {
// floor(x*(range-1) + 0.5) / (range-1)
scale = float((1 << bits) - 1);
offset = 0.0f;
offset0 = 0.5f;
offset1 = 0.0f;
}
else {
// (floor(x*range) + 0.5) / range
scale = float(1 << bits);
offset = 0.5f;
offset0 = 0.0f;
offset1 = 0.5f;
}
if (!dither) {
float * c = img->channel(channel);
const uint count = img->pixelCount();
for (uint i = 0; i < count; i++) {
c[i] = floorf(c[i] * scale + offset) / scale;
c[i] = saturate((floorf(c[i] * scale + offset0) + offset1) / scale);
}
}
else {
@ -2182,7 +2339,7 @@ void Surface::quantize(int channel, int bits, bool exactEndPoints, bool dither)
float & f = img->pixel(channel, x, y, 0);
// Add error and quantize.
float qf = floorf((f + row0[1+x]) * scale + offset) / scale;
float qf = saturate((floorf((f + row0[1+x]) * scale + offset0) + offset1) / scale);
// Compute new error:
float diff = f - qf;
@ -2221,9 +2378,6 @@ void Surface::toNormalMap(float sm, float medium, float big, float large)
const FloatImage * img = m->image;
m->image = nv::createNormalMap(img, (FloatImage::WrapMode)m->wrapMode, filterWeights);
#pragma NV_MESSAGE("TODO: Pack and expand normals explicitly?")
m->image->packNormals(0);
delete img;
m->isNormalMap = true;
@ -2246,7 +2400,6 @@ void Surface::transformNormals(NormalTransform xform)
detach();
FloatImage * img = m->image;
img->expandNormals(0);
const uint count = img->pixelCount();
for (uint i = 0; i < count; i++) {
@ -2308,8 +2461,6 @@ void Surface::transformNormals(NormalTransform xform)
y = n.y;
z = n.z;
}
img->packNormals(0);
}
void Surface::reconstructNormals(NormalTransform xform)
@ -2319,7 +2470,6 @@ void Surface::reconstructNormals(NormalTransform xform)
detach();
FloatImage * img = m->image;
img->expandNormals(0);
const uint count = img->pixelCount();
for (uint i = 0; i < count; i++) {
@ -2357,8 +2507,6 @@ void Surface::reconstructNormals(NormalTransform xform)
y = n.y;
z = n.z;
}
img->packNormals(0);
}
void Surface::toCleanNormalMap()
@ -2367,8 +2515,6 @@ void Surface::toCleanNormalMap()
detach();
m->image->expandNormals(0);
const uint count = m->image->pixelCount();
for (uint i = 0; i < count; i++) {
float x = m->image->pixel(0, i);
@ -2376,22 +2522,48 @@ void Surface::toCleanNormalMap()
m->image->pixel(2, i) = x*x + y*y;
}
m->image->packNormals(0);
}
// [-1,1] -> [ 0,1]
void Surface::packNormals() {
void Surface::packNormals(float scale/*= 0.5f*/, float bias/*= 0.5f*/) {
if (isNull()) return;
detach();
m->image->packNormals(0);
m->image->scaleBias(0, 3, scale, bias);
}
// [ 0,1] -> [-1,1]
void Surface::expandNormals() {
void Surface::expandNormals(float scale/*= 2.0f*/, float bias/*= - 2.0f * 127.0f / 255.0f*/) {
if (isNull()) return;
detach();
m->image->expandNormals(0);
m->image->scaleBias(0, 3, scale, bias);
}
// Create a Toksvig map for this normal map.
// http://blog.selfshadow.com/2011/07/22/specular-showdown/
// @@ Assumes this is a normal map expanded in the [-1, 1] range.
Surface Surface::createToksvigMap(float power) const
{
if (isNull()) return Surface();
// @@ TODO
return Surface();
}
// @@ Should I add support for LEAN maps? That requires 5 terms, which would have to be encoded in two textures.
// There's nothing stopping us from having 5 channels in a surface, and then, let the user swizzle them as they wish.
// CLEAN maps are probably more practical, though.
// http://www.cs.umbc.edu/~olano/papers/lean/
// http://gaim.umbc.edu/2011/07/24/shiny-and-clean/
// http://gaim.umbc.edu/2011/07/26/on-error/
NVTT_API Surface Surface::createCleanMap() const
{
if (isNull()) return Surface();
// @@ TODO
return Surface();
}
@ -2422,7 +2594,7 @@ void Surface::flipZ()
m->image->flipZ();
}
Surface Surface::subImage(int x0, int x1, int y0, int y1, int z0, int z1) const
Surface Surface::createSubImage(int x0, int x1, int y0, int y1, int z0, int z1) const
{
Surface s;
@ -2495,9 +2667,6 @@ bool Surface::addChannel(const Surface & srcImage, int srcChannel, int dstChanne
dst = m->image;
const uint w = src->width();
const uint h = src->height();
float * d = dst->channel(dstChannel);
const float * s = src->channel(srcChannel);
@ -2510,6 +2679,38 @@ bool Surface::addChannel(const Surface & srcImage, int srcChannel, int dstChanne
}
bool Surface::copy(const Surface & srcImage, int xsrc, int ysrc, int zsrc, int xsize, int ysize, int zsize, int xdst, int ydst, int zdst)
{
if (xsrc < 0 || ysrc < 0 || zsrc < 0) return false;
if (xdst < 0 || ydst < 0 || zdst < 0) return false;
FloatImage * dst = m->image;
const FloatImage * src = srcImage.m->image;
if (toU32(xsrc + xsize) > src->width() || toU32(ysrc + ysize) > src->height() || toU32(zsrc + zsize) > src->depth()) return false;
if (toU32(xdst + xsize) > dst->width() || toU32(ydst + ysize) > dst->height() || toU32(zdst + zsize) > dst->depth()) return false;
detach();
// For each channel.
for(int i = 0; i < 4; i++) {
float * d = dst->channel(i);
const float * s = src->channel(i);
// Copy region from src to dst.
for (int z = 0; z < zsize; z++) {
for (int y = 0; y < ysize; y++) {
for (int x = 0; x < xsize; x++) {
d[dst->index(xdst + x, ydst + y, zdst + z)] = s[src->index(xsrc + x, ysrc + y, zsrc + z)];
}
}
}
}
return true;
}
float nvtt::rmsError(const Surface & reference, const Surface & image)
{

View File

@ -78,8 +78,10 @@ namespace nvtt
} // nvtt namespace
namespace nv {
bool canMakeNextMipmap(uint w, uint h, uint d, uint min_size);
uint countMipmaps(uint w);
uint countMipmaps(uint w, uint h, uint d);
uint countMipmapsWithMinSize(uint w, uint h, uint d, uint min_size);
uint computeImageSize(uint w, uint h, uint d, uint bitCount, uint alignmentInBytes, nvtt::Format format);
void getTargetExtent(int * w, int * h, int * d, int maxExtent, nvtt::RoundMode roundMode, nvtt::TextureType textureType);
}

View File

@ -454,15 +454,18 @@ namespace nvtt
NVTT_API AlphaMode alphaMode() const;
NVTT_API bool isNormalMap() const;
NVTT_API int countMipmaps() const;
NVTT_API int countMipmaps(int min_size) const;
NVTT_API float alphaTestCoverage(float alphaRef = 0.5) const;
NVTT_API float average(int channel, int alpha_channel = -1, float gamma = 2.2f) const;
NVTT_API const float * data() const;
NVTT_API const float * channel(int i) const;
NVTT_API void histogram(int channel, float rangeMin, float rangeMax, int binCount, int * binPtr) const;
NVTT_API void range(int channel, float * rangeMin, float * rangeMax) const;
NVTT_API void range(int channel, float * rangeMin, float * rangeMax, int alpha_channel = -1, float alpha_ref = 0.f) const;
// Texture data.
NVTT_API bool load(const char * fileName, bool * hasAlpha = 0);
NVTT_API bool save(const char * fileName) const;
NVTT_API bool save(const char * fileName, bool hasAlpha = 0, bool hdr = 0) const;
NVTT_API bool setImage(int w, int h, int d);
NVTT_API bool setImage(InputFormat format, int w, int h, int d, const void * data);
NVTT_API bool setImage(InputFormat format, int w, int h, int d, const void * r, const void * g, const void * b, const void * a);
NVTT_API bool setImage2D(Format format, Decoder decoder, int w, int h, const void * data);
@ -472,9 +475,14 @@ namespace nvtt
NVTT_API void resize(int w, int h, int d, ResizeFilter filter, float filterWidth, const float * params = 0);
NVTT_API void resize(int maxExtent, RoundMode mode, ResizeFilter filter);
NVTT_API void resize(int maxExtent, RoundMode mode, ResizeFilter filter, float filterWidth, const float * params = 0);
NVTT_API bool buildNextMipmap(MipmapFilter filter);
NVTT_API bool buildNextMipmap(MipmapFilter filter, float filterWidth, const float * params = 0);
NVTT_API void resize_make_square(int maxExtent, RoundMode roundMode, ResizeFilter filter);
NVTT_API bool buildNextMipmap(MipmapFilter filter, int min_size = 1);
NVTT_API bool buildNextMipmap(MipmapFilter filter, float filterWidth, const float * params = 0, int min_size = 1);
NVTT_API bool buildNextMipmapSolidColor(const float * const color_components);
NVTT_API void canvasSize(int w, int h, int d);
// associated to resizing:
NVTT_API bool canMakeNextMipmap(int min_size = 1);
// Color transforms.
NVTT_API void toLinear(float gamma);
@ -488,17 +496,15 @@ namespace nvtt
NVTT_API void swizzle(int r, int g, int b, int a);
NVTT_API void scaleBias(int channel, float scale, float bias);
NVTT_API void clamp(int channel, float low = 0.0f, float high = 1.0f);
NVTT_API void packNormal();
NVTT_API void expandNormal();
NVTT_API void blend(float r, float g, float b, float a, float t);
NVTT_API void premultiplyAlpha();
NVTT_API void toGreyScale(float redScale, float greenScale, float blueScale, float alphaScale);
NVTT_API void setBorder(float r, float g, float b, float a);
NVTT_API void fill(float r, float g, float b, float a);
NVTT_API void scaleAlphaToCoverage(float coverage, float alphaRef = 0.5f);
//NVTT_API bool normalizeRange(float * rangeMin, float * rangeMax);
NVTT_API void toRGBM(float range = 1.0f, float threshold = 0.0f);
NVTT_API void fromRGBM(float range = 1.0f);
NVTT_API void toLM(float range = 1.0f, float threshold = 0.0f);
NVTT_API void toRGBE(int mantissaBits, int exponentBits);
NVTT_API void fromRGBE(int mantissaBits, int exponentBits);
NVTT_API void toYCoCg();
@ -519,14 +525,14 @@ namespace nvtt
NVTT_API void binarize(int channel, float threshold, bool dither);
NVTT_API void quantize(int channel, int bits, bool exactEndPoints, bool dither);
// Normal map transforms. @@ All these methods assume packed normals.
// Normal map transforms.
NVTT_API void toNormalMap(float sm, float medium, float big, float large);
NVTT_API void normalizeNormalMap();
NVTT_API void transformNormals(NormalTransform xform);
NVTT_API void reconstructNormals(NormalTransform xform);
NVTT_API void toCleanNormalMap();
NVTT_API void packNormals(); // [-1,1] -> [ 0,1]
NVTT_API void expandNormals(); // [ 0,1] -> [-1,1]
NVTT_API void packNormals(float scale = 0.5f, float bias = 0.5f); // [-1,1] -> [ 0,1]
NVTT_API void expandNormals(float scale = 2.0f, float bias = -1.0f); // [ 0,1] -> [-1,1]
NVTT_API Surface createToksvigMap(float power) const;
NVTT_API Surface createCleanMap() const;
@ -534,7 +540,7 @@ namespace nvtt
NVTT_API void flipX();
NVTT_API void flipY();
NVTT_API void flipZ();
NVTT_API Surface subImage(int x0, int x1, int y0, int y1, int z0, int z1) const;
NVTT_API Surface createSubImage(int x0, int x1, int y0, int y1, int z0, int z1) const;
// Copy image data.
NVTT_API bool copyChannel(const Surface & srcImage, int srcChannel);
@ -542,6 +548,9 @@ namespace nvtt
NVTT_API bool addChannel(const Surface & img, int srcChannel, int dstChannel, float scale);
NVTT_API bool copy(const Surface & src, int xsrc, int ysrc, int zsrc, int xsize, int ysize, int zsize, int xdst, int ydst, int zdst);
//private:
void detach();
@ -599,12 +608,15 @@ namespace nvtt
NVTT_API float average(int channel) const;
NVTT_API void range(int channel, float * minimum_ptr, float * maximum_ptr) const;
NVTT_API void clamp(int channel, float low = 0.0f, float high = 1.0f);
// Filtering.
NVTT_API CubeSurface irradianceFilter(int size, EdgeFixup fixupMethod) const;
NVTT_API CubeSurface cosinePowerFilter(int size, float cosinePower, EdgeFixup fixupMethod) const;
NVTT_API CubeSurface fastResample(int size, EdgeFixup fixupMethod) const;
/*
NVTT_API void resize(int w, int h, ResizeFilter filter);

View File

@ -856,7 +856,7 @@ int main(int argc, char *argv[])
outputFileName.stripExtension();
if (set.type == ImageType_HDR) outputFileName.append(".dds");
else outputFileName.append(".tga");
if (!img_out.save(outputFileName.str()))
if (!img_out.save(outputFileName.str(), set.type == ImageType_RGBA, set.type == ImageType_HDR))
{
printf("Error saving file '%s'.\n", outputFileName.str());
}

View File

@ -58,7 +58,7 @@ struct MyAssertHandler : public nv::AssertHandler {
}
// Handler method, note that func might be NULL!
virtual int assertion( const char *exp, const char *file, int line, const char *func ) {
virtual int assertion( const char *exp, const char *file, int line, const char *func, const char *msg, va_list arg ) {
fprintf(stderr, "Assertion failed: %s\nIn %s:%d\n", exp, file, line);
nv::debug::dumpInfo();
exit(1);