void PtexReader::getPixel(int faceid, int u, int v, float* result, int firstchan, int nchannels, Ptex::Res res) { memset(result, 0, nchannels); // clip nchannels against actual number available nchannels = PtexUtils::min(nchannels, _header.nchannels-firstchan); if (nchannels <= 0) return; // get raw pixel data PtexPtr<PtexFaceData> data ( getData(faceid, res) ); if (!data) return; void* pixel = alloca(_pixelsize); data->getPixel(u, v, pixel); // adjust for firstchan offset int datasize = DataSize(_header.datatype); if (firstchan) pixel = (char*) pixel + datasize * firstchan; // convert/copy to result as needed if (_header.datatype == dt_float) memcpy(result, pixel, datasize * nchannels); else ConvertToFloat(result, pixel, _header.datatype, nchannels); }
int main(int argc, char** argv) { int maxmem = argc >= 2 ? atoi(argv[1]) : 1024*1024; PtexCache* c = PtexCache::create(0, maxmem); Ptex::String error; PtexPtr<PtexTexture> r ( c->get("test.ptx", error) ); if (!r) { std::cerr << error.c_str() << std::endl; return 1; } PtexFilter::Options opts(PtexFilter::f_bicubic, 0, 1.0); PtexPtr<PtexFilter> f ( PtexFilter::getFilter(r, opts) ); float result[4]; int faceid = 0; float u=0, v=0, uw=.125, vw=.125; for (v = 0; v <= 1; v += .125) { for (u = 0; u <= 1; u += .125) { f->eval(result, 0, 1, faceid, u, v, uw, 0, 0, vw); printf("%8f %8f -> %8f\n", u, v, result[0]); } } return 0; }
void PtexReader::TiledFaceBase::getPixel(int ui, int vi, void* result) { int tileu = ui >> _tileres.ulog2; int tilev = vi >> _tileres.vlog2; PtexPtr<PtexFaceData> tile ( getTile(tilev * _ntilesu + tileu) ); tile->getPixel(ui - (tileu<<_tileres.ulog2), vi - (tilev<<_tileres.vlog2), result); }
PTEX_NAMESPACE_BEGIN void PtexTriangleFilter::eval(float* result, int firstChan, int nChannels, int faceid, float u, float v, float uw1, float vw1, float uw2, float vw2, float width, float blur) { // init if (!_tx || nChannels <= 0) return; if (faceid < 0 || faceid >= _tx->numFaces()) return; _ntxchan = _tx->numChannels(); _dt = _tx->dataType(); _firstChanOffset = firstChan*DataSize(_dt); _nchan = PtexUtils::min(nChannels, _ntxchan-firstChan); // get face info const FaceInfo& f = _tx->getFaceInfo(faceid); // if neighborhood is constant, just return constant value of face if (f.isNeighborhoodConstant()) { PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) ); if (data) { char* d = (char*) data->getData() + _firstChanOffset; Ptex::ConvertToFloat(result, d, _dt, _nchan); } return; } // clamp u and v u = PtexUtils::clamp(u, 0.0f, 1.0f); v = PtexUtils::clamp(v, 0.0f, 1.0f); // build kernel PtexTriangleKernel k; buildKernel(k, u, v, uw1, vw1, uw2, vw2, width, blur, f.res); // accumulate the weight as we apply _weight = 0; // allocate temporary result _result = (float*) alloca(sizeof(float)*_nchan); memset(_result, 0, sizeof(float)*_nchan); // apply to faces splitAndApply(k, faceid, f); // normalize (both for data type and cumulative kernel weight applied) // and output result float scale = 1.0f / (_weight * OneValue(_dt)); for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale); // clear temp result _result = 0; }
void PtexTriangleFilter::applyIter(PtexTriangleKernelIter& k, PtexFaceData* dh) { if (dh->isConstant()) { k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan); _weight += k.weight; } else if (dh->isTiled()) { Ptex::Res tileres = dh->tileRes(); PtexTriangleKernelIter kt = k; int tileresu = tileres.u(); int tileresv = tileres.v(); kt.rowlen = tileresu; int ntilesu = k.rowlen / kt.rowlen; int wOffsetBase = k.rowlen - tileresu; for (int tilev = k.v1 / tileresv, tilevEnd = (k.v2-1) / tileresv; tilev <= tilevEnd; tilev++) { int vOffset = tilev * tileresv; kt.v = k.v - (float)vOffset; kt.v1 = PtexUtils::max(0, k.v1 - vOffset); kt.v2 = PtexUtils::min(k.v2 - vOffset, tileresv); for (int tileu = k.u1 / tileresu, tileuEnd = (k.u2-1) / tileresu; tileu <= tileuEnd; tileu++) { int uOffset = tileu * tileresu; int wOffset = wOffsetBase - uOffset - vOffset; kt.u = k.u - (float)uOffset; kt.u1 = PtexUtils::max(0, k.u1 - uOffset); kt.u2 = PtexUtils::min(k.u2 - uOffset, tileresu); kt.w1 = k.w1 - wOffset; kt.w2 = k.w2 - wOffset; PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) ); if (th) { kt.weight = 0; if (th->isConstant()) kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan); else kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan); _weight += kt.weight; } } } } else { k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan); _weight += k.weight; } }
void PtexMainWriter::finish() { // do nothing if there's no new data to write if (!_hasNewData) return; // copy missing faces from _reader if (_reader) { for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) { if (_faceinfo[i].flags == uint8_t(-1)) { // copy face data const Ptex::FaceInfo& info = _reader->getFaceInfo(i); int size = _pixelSize * info.res.size(); if (info.isConstant()) { PtexPtr<PtexFaceData> data ( _reader->getData(i) ); if (data) { writeConstantFace(i, info, data->getData()); } } else { void* data = malloc(size); _reader->getData(i, data, 0); writeFace(i, info, data, 0); free(data); } } } } else { // just flag missing faces as constant (black) for (int i = 0, nfaces = _header.nfaces; i < nfaces; i++) { if (_faceinfo[i].flags == uint8_t(-1)) _faceinfo[i].flags = FaceInfo::flag_constant; } } // write reductions to tmp file if (_genmipmaps) generateReductions(); // flag faces w/ constant neighborhoods flagConstantNeighorhoods(); // update header _header.nlevels = uint16_t(_levels.size()); _header.nfaces = uint32_t(_faceinfo.size()); // create new file FILE* newfp = fopen(_newpath.c_str(), "wb+"); if (!newfp) { setError(fileError("Can't write to ptex file: ", _newpath.c_str())); return; } // write blank header (to fill in later) writeBlank(newfp, HeaderSize); writeBlank(newfp, ExtHeaderSize); // write compressed face info block _header.faceinfosize = writeZipBlock(newfp, &_faceinfo[0], sizeof(FaceInfo)*_header.nfaces); // write compressed const data block _header.constdatasize = writeZipBlock(newfp, &_constdata[0], int(_constdata.size())); // write blank level info block (to fill in later) FilePos levelInfoPos = ftello(newfp); writeBlank(newfp, LevelInfoSize * _header.nlevels); // write level data blocks (and record level info) std::vector<LevelInfo> levelinfo(_header.nlevels); for (int li = 0; li < _header.nlevels; li++) { LevelInfo& info = levelinfo[li]; LevelRec& level = _levels[li]; int nfaces = int(level.fdh.size()); info.nfaces = nfaces; // output compressed level data header info.levelheadersize = writeZipBlock(newfp, &level.fdh[0], sizeof(FaceDataHeader)*nfaces); info.leveldatasize = info.levelheadersize; // copy level data from tmp file for (int fi = 0; fi < nfaces; fi++) info.leveldatasize += copyBlock(newfp, _tmpfp, level.pos[fi], level.fdh[fi].blocksize()); _header.leveldatasize += info.leveldatasize; } rewind(_tmpfp); // write meta data (if any) if (!_metadata.empty()) writeMetaData(newfp); // update extheader for edit data position _extheader.editdatapos = ftello(newfp); // rewrite level info block fseeko(newfp, levelInfoPos, SEEK_SET); _header.levelinfosize = writeBlock(newfp, &levelinfo[0], LevelInfoSize*_header.nlevels); // rewrite header fseeko(newfp, 0, SEEK_SET); writeBlock(newfp, &_header, HeaderSize); writeBlock(newfp, &_extheader, ExtHeaderSize); fclose(newfp); }
PtexFaceData* PtexReader::getData(int faceid, Res res) { if (!_ok) return 0; if (faceid < 0 || size_t(faceid) >= _header.nfaces) return 0; FaceInfo& fi = _faceinfo[faceid]; if ((fi.isConstant() && res >= 0) || res == 0) { return new ConstDataPtr(getConstData() + faceid * _pixelsize, _pixelsize); } // lock cache (can't autolock since we might need to unlock early) _cache->cachelock.lock(); // determine how many reduction levels are needed int redu = fi.res.ulog2 - res.ulog2, redv = fi.res.vlog2 - res.vlog2; if (redu == 0 && redv == 0) { // no reduction - get level zero (full) res face Level* level = getLevel(0); FaceData* face = getFace(0, level, faceid); level->unref(); _cache->cachelock.unlock(); return face; } if (redu == redv && !fi.hasEdits() && res >= 0) { // reduction is symmetric and non-negative // and face has no edits => access data from reduction level (if present) int levelid = redu; if (size_t(levelid) < _levels.size()) { Level* level = getLevel(levelid); // get reduction face id int rfaceid = _rfaceids[faceid]; // get the face data (if present) FaceData* face = 0; if (size_t(rfaceid) < level->faces.size()) face = getFace(levelid, level, rfaceid); level->unref(); if (face) { _cache->cachelock.unlock(); return face; } } } // dynamic reduction required - look in dynamic reduction cache FaceData*& face = _reductions[ReductionKey(faceid, res)]; if (face) { face->ref(); _cache->cachelock.unlock(); return face; } // not found, generate new reduction // unlock cache - getData and reduce will handle their own locking _cache->cachelock.unlock(); if (res.ulog2 < 0 || res.vlog2 < 0) { std::cerr << "PtexReader::getData - reductions below 1 pixel not supported" << std::endl; return 0; } if (redu < 0 || redv < 0) { std::cerr << "PtexReader::getData - enlargements not supported" << std::endl; return 0; } if (_header.meshtype == mt_triangle) { if (redu != redv) { std::cerr << "PtexReader::getData - anisotropic reductions not supported for triangle mesh" << std::endl; return 0; } PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2+1, res.vlog2+1)) ); FaceData* src = dynamic_cast<FaceData*>(psrc.get()); assert(src); if (src) src->reduce(face, this, res, PtexUtils::reduceTri); return face; } // determine which direction to blend bool blendu; if (redu == redv) { // for symmetric face blends, alternate u and v blending blendu = (res.ulog2 & 1); } else blendu = redu > redv; if (blendu) { // get next-higher u-res and reduce in u PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2+1, res.vlog2)) ); FaceData* src = dynamic_cast<FaceData*>(psrc.get()); assert(src); if (src) src->reduce(face, this, res, PtexUtils::reduceu); } else { // get next-higher v-res and reduce in v PtexPtr<PtexFaceData> psrc ( getData(faceid, Res(res.ulog2, res.vlog2+1)) ); FaceData* src = dynamic_cast<FaceData*>(psrc.get()); assert(src); if (src) src->reduce(face, this, res, PtexUtils::reducev); } return face; }
void PtexReader::getData(int faceid, void* buffer, int stride, Res res) { if (!_ok) return; // note - all locking is handled in called getData methods int resu = res.u(), resv = res.v(); int rowlen = _pixelsize * resu; if (stride == 0) stride = rowlen; PtexPtr<PtexFaceData> d ( getData(faceid, res) ); if (!d) return; if (d->isConstant()) { // fill dest buffer with pixel value PtexUtils::fill(d->getData(), buffer, stride, resu, resv, _pixelsize); } else if (d->isTiled()) { // loop over tiles Res tileres = d->tileRes(); int ntilesu = res.ntilesu(tileres); int ntilesv = res.ntilesv(tileres); int tileures = tileres.u(); int tilevres = tileres.v(); int tilerowlen = _pixelsize * tileures; int tile = 0; char* dsttilerow = (char*) buffer; for (int i = 0; i < ntilesv; i++) { char* dsttile = dsttilerow; for (int j = 0; j < ntilesu; j++) { PtexPtr<PtexFaceData> t ( d->getTile(tile++) ); if (!t) { i = ntilesv; break; } if (t->isConstant()) PtexUtils::fill(t->getData(), dsttile, stride, tileures, tilevres, _pixelsize); else PtexUtils::copy(t->getData(), tilerowlen, dsttile, stride, tilevres, tilerowlen); dsttile += tilerowlen; } dsttilerow += stride * tilevres; } } else { PtexUtils::copy(d->getData(), rowlen, buffer, stride, resv, rowlen); } }
int main(int argc, char** argv) { bool showver = 0; bool dumpmeta = 0; bool dumpfaceinfo = 0; bool dumpdata = 0; bool dumpalldata = 0; bool dumpinternal = 0; bool dumptiling = 0; bool checkadjacency = 0; const char* fname = 0; while (--argc) { if (**++argv == '-') { char* cp = *argv + 1; if (!*cp) usage(); // handle bare '-' while (*cp) { switch (*cp++) { case 'v': showver = 1; break; case 'm': dumpmeta = 1; break; case 'd': dumpdata = 1; break; case 'D': dumpdata = 1; dumpalldata = 1; break; case 'f': dumpfaceinfo = 1; break; case 't': dumptiling = 1; break; case 'i': dumpinternal = 1; break; case 'c': checkadjacency = 1; break; default: usage(); } } } else if (fname) usage(); else fname = *argv; } if (showver) { #ifdef PTEX_VENDOR # define str(s) #s # define xstr(s) str(s) std::cout << "Ptex v" << PtexLibraryMajorVersion << "." << PtexLibraryMinorVersion << "-" << xstr(PTEX_VENDOR) << std::endl; #else std::cout << "Ptex v" << PtexLibraryMajorVersion << "." << PtexLibraryMinorVersion << std::endl; #endif } if (!fname) { if (!showver) usage(); return 0; } Ptex::String error; PtexPtr<PtexTexture> r ( PtexTexture::open(fname, error) ); if (!r) { std::cerr << error.c_str() << std::endl; return 1; } if (checkadjacency) { return CheckAdjacency(r); } std::cout << "meshType: " << Ptex::MeshTypeName(r->meshType()) << std::endl; std::cout << "dataType: " << Ptex::DataTypeName(r->dataType()) << std::endl; std::cout << "numChannels: " << r->numChannels() << std::endl; std::cout << "alphaChannel: "; if (r->alphaChannel() == -1) std::cout << "(none)" << std::endl; else std::cout << r->alphaChannel() << std::endl; std::cout << "uBorderMode: " << Ptex::BorderModeName(r->uBorderMode()) << std::endl; std::cout << "vBorderMode: " << Ptex::BorderModeName(r->vBorderMode()) << std::endl; std::cout << "edgeFilterMode: " << Ptex::EdgeFilterModeName(r->edgeFilterMode()) << std::endl; std::cout << "numFaces: " << r->numFaces() << std::endl; std::cout << "hasEdits: " << (r->hasEdits() ? "yes" : "no") << std::endl; std::cout << "hasMipMaps: " << (r->hasMipMaps() ? "yes" : "no") << std::endl; PtexPtr<PtexMetaData> meta ( r->getMetaData() ); if (meta) { std::cout << "numMetaKeys: " << meta->numKeys() << std::endl; if (dumpmeta && meta->numKeys()) DumpMetaData(meta); } if (dumpfaceinfo || dumpdata || dumptiling) { uint64_t texels = 0; for (int i = 0; i < r->numFaces(); i++) { std::cout << "face " << i << ":"; const Ptex::FaceInfo& f = r->getFaceInfo(i); DumpFaceInfo(f); texels += f.res.size(); if (dumptiling) { PtexPtr<PtexFaceData> dh ( r->getData(i, f.res) ); DumpTiling(dh); } if (dumpdata) DumpData(r, i, dumpalldata); } std::cout << "texels: " << texels << std::endl; } if (dumpinternal) DumpInternal(r); return 0; }
void PtexSeparableFilter::eval(float* result, int firstChan, int nChannels, int faceid, float u, float v, float uw1, float vw1, float uw2, float vw2, float width, float blur) { // init if (!_tx || nChannels <= 0) return; if (faceid < 0 || faceid >= _tx->numFaces()) return; _ntxchan = _tx->numChannels(); _dt = _tx->dataType(); _firstChanOffset = firstChan*DataSize(_dt); _nchan = PtexUtils::min(nChannels, _ntxchan-firstChan); // get face info const FaceInfo& f = _tx->getFaceInfo(faceid); // if neighborhood is constant, just return constant value of face if (f.isNeighborhoodConstant()) { PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) ); if (data) { char* d = (char*) data->getData() + _firstChanOffset; Ptex::ConvertToFloat(result, d, _dt, _nchan); } return; } // find filter width as bounding box of vectors w1 and w2 float uw = fabs(uw1) + fabs(uw2), vw = fabs(vw1) + fabs(vw2); // handle border modes switch (_uMode) { case m_clamp: u = PtexUtils::clamp(u, 0.0f, 1.0f); break; case m_periodic: u = u-floor(u); break; case m_black: break; // do nothing } switch (_vMode) { case m_clamp: v = PtexUtils::clamp(v, 0.0f, 1.0f); break; case m_periodic: v = v-floor(v); case m_black: break; // do nothing } // build kernel PtexSeparableKernel k; if (f.isSubface()) { // for a subface, build the kernel as if it were on a main face and then downres uw = uw * width + blur * 2; vw = vw * width + blur * 2; buildKernel(k, u*.5, v*.5, uw*.5, vw*.5, f.res); if (k.res.ulog2 == 0) k.upresU(); if (k.res.vlog2 == 0) k.upresV(); k.res.ulog2--; k.res.vlog2--; } else { uw = uw * width + blur; vw = vw * width + blur; buildKernel(k, u, v, uw, vw, f.res); } k.stripZeros(); // check kernel (debug only) assert(k.uw > 0 && k.vw > 0); assert(k.uw <= PtexSeparableKernel::kmax && k.vw <= PtexSeparableKernel::kmax); _weight = k.weight(); // allocate temporary double-precision result _result = (double*) alloca(sizeof(double)*_nchan); memset(_result, 0, sizeof(double)*_nchan); // apply to faces splitAndApply(k, faceid, f); // normalize (both for data type and cumulative kernel weight applied) // and output result double scale = 1.0 / (_weight * OneValue(_dt)); for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale); // clear temp result _result = 0; }
void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f) { assert(k.u >= 0 && k.u + k.uw <= k.res.u()); assert(k.v >= 0 && k.v + k.vw <= k.res.v()); if (k.uw <= 0 || k.vw <= 0) return; // downres kernel if needed while (k.res.u() > f.res.u()) k.downresU(); while (k.res.v() > f.res.v()) k.downresV(); // get face data, and apply PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) ); if (!dh) return; if (dh->isConstant()) { k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan); } else if (dh->isTiled()) { Ptex::Res tileres = dh->tileRes(); PtexSeparableKernel kt; kt.res = tileres; int tileresu = tileres.u(); int tileresv = tileres.v(); int ntilesu = k.res.u() / tileresu; for (int v = k.v, vw = k.vw; vw > 0; vw -= kt.vw, v += kt.vw) { int tilev = v / tileresv; kt.v = v % tileresv; kt.vw = PtexUtils::min(vw, tileresv - kt.v); kt.kv = k.kv + v - k.v; for (int u = k.u, uw = k.uw; uw > 0; uw -= kt.uw, u += kt.uw) { int tileu = u / tileresu; kt.u = u % tileresu; kt.uw = PtexUtils::min(uw, tileresu - kt.u); kt.ku = k.ku + u - k.u; PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) ); if (th) { if (th->isConstant()) kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan); else kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan); } } } } else { k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan); } }
void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f) { assert(k.u >= 0 && k.u + k.uw <= k.res.u()); assert(k.v >= 0 && k.v + k.vw <= k.res.v()); if (k.uw <= 0 || k.vw <= 0) return; // downres kernel if needed while (k.res.u() > f.res.u()) k.downresU(); while (k.res.v() > f.res.v()) k.downresV(); // get face data, and apply PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) ); if (!dh) return; if (dh->isConstant()) { k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan); return; } // allocate temporary result for tanvec mode (if needed) bool tanvecMode = (_efm == efm_tanvec) && (_nchan >= 2) && (k.rot > 0); float* result = tanvecMode ? (float*) alloca(sizeof(float)*_nchan) : _result; if (tanvecMode) memset(result, 0, sizeof(float)*_nchan); if (dh->isTiled()) { Ptex::Res tileres = dh->tileRes(); PtexSeparableKernel kt; kt.res = tileres; int tileresu = tileres.u(); int tileresv = tileres.v(); int ntilesu = k.res.u() / tileresu; for (int v = k.v, vw = k.vw; vw > 0; vw -= kt.vw, v += kt.vw) { int tilev = v / tileresv; kt.v = v % tileresv; kt.vw = PtexUtils::min(vw, tileresv - kt.v); kt.kv = k.kv + v - k.v; for (int u = k.u, uw = k.uw; uw > 0; uw -= kt.uw, u += kt.uw) { int tileu = u / tileresu; kt.u = u % tileresu; kt.uw = PtexUtils::min(uw, tileresu - kt.u); kt.ku = k.ku + u - k.u; PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) ); if (th) { if (th->isConstant()) kt.applyConst(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan); else kt.apply(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan); } } } } else { k.apply(result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan); } if (tanvecMode) { // rotate tangent-space vector data and update main result switch (k.rot) { case 0: // rot==0 included for completeness, but tanvecMode should be false in this case _result[0] += result[0]; _result[1] += result[1]; break; case 1: _result[0] -= result[1]; _result[1] += result[0]; break; case 2: _result[0] -= result[0]; _result[1] -= result[1]; break; case 3: _result[0] += result[1]; _result[1] -= result[0]; break; } for (int i = 2; i < _nchan; i++) _result[i] += result[i]; } }