/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2012 The Foundry Visionmongers Ltd. All Rights Reserved. // // // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// #include "DDImage/DeepReader.h" #include "DDImage/DeepPlane.h" #include "DDImage/Knobs.h" #include "DDImage/Thread.h" #include "dtex.h" #include "rx.h" using namespace DD::Image; static const char* kOpacityType[] = { "auto", "deepopacity", "alpha", nullptr }; enum OpacityType { eAuto = 0, eDeepOpacity, eAlpha } ; namespace Nuke { namespace Deep { static bool endswith(const std::string& target, const std::string& suffix) { if (target.length() < suffix.length()) return false; return target.substr(target.length() - suffix.length()) == suffix; } static inline bool IsRGB(Channel channel) { return channel == Chan_Red || channel == Chan_Blue || channel == Chan_Green; } static inline bool IsRGBA(Channel channel) { return channel == Chan_Red || channel == Chan_Blue || channel == Chan_Green || channel == Chan_Alpha; } /** * Fetch the major version of renderman. Returns -1 if indeterminable. */ static int RenderManVersion() { int version[4]; RxInfoType_t type; int count; int status = RxRendererInfo("version", &version, 4 * sizeof(int), &type, &count); if (type == RxInfoInteger && count == 4 && status == 0) return version[1]; else return -1; } class dtexDeepReaderFormat : public DeepReaderFormat { friend class dtexDeepReader; bool _raw; bool _premult; bool _discrete; int _type; public: dtexDeepReaderFormat() { _raw = false; _premult = false; _discrete = true; // NOTE this is for backwards compatibility, it is knob defaulted to false ( which is probably more correct and matches weta's implementation ) _type = (int) eAuto; } void knobs(Knob_Callback f) override { Enumeration_knob(f, &_type, kOpacityType, "type"); Tooltip(f, "The type of dtex file. \n" "If this is set to deepopacity, the dtex file will be interpreted as " "though it were rendered as an accumulated deep opacity " "file, corresponding to a Renderman Display Driver config " "of:
" "    Display \"Filename.dtex\" \"deepshad\" \"deepopacity\"
" "
If this is set to alpha then it will be interpreted as the newer point-sampled alpha or color which " "would be a display line like:
" "    Display \"Filename.dtex\" \"deepshad\" \"a\"
" "or...
" "    Display \"Filename.dtex\" \"deepshad\" \"rgba\"
" "
If set to 'auto' the type will be autodetected by looking at the subimage name. If it is either \"Deep Shadow\" " "or ends with (or is) \".deepopacity\" the file will be treated like a deep opacity file." ); Bool_knob(f, &_discrete, "discrete", "discrete"); Tooltip(f, "Treat the file as discrete samples with the front and back being the same. This is only relevant for deep opacity files, color deep compositing files are always discrete."); Bool_knob(f, &_premult, "premult", "premultiply"); Tooltip(f, "Premultiply values from file (if off, then it assumes they are already premultiplied)"); Bool_knob(f, &_raw, "raw", "raw values"); Tooltip(f, "Read the samples exactly 'as is' without processing."); } void append(Hash& hash) override { hash.append(_raw); hash.append(_premult); hash.append(_discrete); hash.append(_type); } }; /** * This is a plugin to read the Pixar Renderman 'dtex' files. */ class dtexDeepReader : public DeepReader { static Lock sLibraryLock; DtexFile* _dtexFile; DtexCache* _dtexCache; Lock _engineLock; int _numChans; DD::Image::OutputContext _outputContext; int _dtexOpenError; // defined in thirdparty/sony/renderman/version/bin/platform/include/dtex.h dtexDeepReaderFormat *_dtexReaderFormat; public: dtexDeepReader(DeepReaderOwner* op, const std::string& filename) : DeepReader(op) { _dtexOpenError = DTEX_NOERR; _dtexFile = nullptr; _dtexCache = nullptr; if (filename.length()) open(filename); if (!_dtexFile) { if (filename.length() == 0) _op->error("no filename specified"); else if (_dtexOpenError != DTEX_NOERR) _op->error("error (%d) opening file: %s", _dtexOpenError, filename.c_str()); else _op->error("missing file: %s", filename.c_str()); _deepInfo = DeepInfo(); return; } _outputContext = _owner->readerOutputContext(); DtexImage* image; DtexGetImageByIndex(_dtexFile, 0, &image); float NP[16]; DtexNP(image, NP); float Nl[16]; DtexNl(image, Nl); _numChans = DtexNumChan(image); setInfo(DtexWidth(image), DtexHeight(image), _outputContext, Mask_RGB | Mask_Alpha | Mask_Deep); _metaData.setData("dtex/np", NP, 16); _metaData.setData("dtex/nl", Nl, 16); _dtexReaderFormat = dynamic_cast( op->handler() ); mFnAssert(_dtexReaderFormat); } ~dtexDeepReader() override { if (_dtexFile) { DtexClose(_dtexFile); _dtexFile = nullptr; } if (_dtexCache) { DtexDestroyCache(_dtexCache); _dtexCache = nullptr; } } void open(const std::string& filename) { _dtexCache = DtexCreateCache(10000, nullptr); _dtexOpenError = DtexOpenFile(filename.c_str(), "rb", _dtexCache, &_dtexFile); } bool doDeepEngine(DD::Image::Box box, const ChannelSet& channels, DeepOutputPlane& plane) override { static int sRenderManVersion = RenderManVersion(); // RenderMan 15 seems not to be thread-safe (bug 23831) Guard g(sRenderManVersion < 16 ? sLibraryLock : _engineLock); if (!_dtexFile) { if (_dtexOpenError != DTEX_NOERR) _op->error("error (%d) opening file", _dtexOpenError); else _op->error("missing file."); return false; } DtexImage* image; DtexGetImageByIndex(_dtexFile, 0, &image); int height = DtexHeight(image); DtexPixel* pixel = DtexMakePixel(_numChans); plane = DeepOutputPlane(channels, box); bool raw = _dtexReaderFormat->_raw; bool discrete = _dtexReaderFormat->_discrete; bool premult = _dtexReaderFormat->_premult; enum OpacityType type = (enum OpacityType)_dtexReaderFormat->_type; std::map chanMap; if (_numChans == 4) { chanMap[Chan_Red] = 0; chanMap[Chan_Green] = 1; chanMap[Chan_Blue] = 2; chanMap[Chan_Alpha] = 3; } else { chanMap[Chan_Alpha] = 0; } std::vector pts(_numChans); std::vector pts2(_numChans); for (Box::iterator it = box.begin(); it != box.end(); it++) { float x = it.x; float y = it.y; _outputContext.from_proxy_xy(x, y); DtexGetPixel(image, int(x), height - 1 - int(y), pixel); // try and work out the type automatically if ( type == eAuto ) { type = eAlpha; char* imageName = DtexImageName( image ); if ( imageName && strlen(imageName) ) { std::string name( imageName ); if ( name == "Deep Shadow" ) type = eDeepOpacity; else if ( endswith( name, ".deepopacity" ) ) type = eDeepOpacity; } } int numpts = DtexPixelGetNumPoints(pixel); DeepOutPixel pels(numpts * channels.size()); for (int j = 0; j < numpts; j ++ ) { float z; DtexPixelGetPoint(pixel, j, &z, &pts[0]); float z2; if ( raw || type == eAlpha ) { z2 = z; } else { if ( DtexPixelGetPoint(pixel, j+1, &z2, &pts2[0]) != DTEX_NOERR ) continue; if ( pts[chanMap[Chan_Alpha]] == pts2[chanMap[Chan_Alpha]] ) continue; if ( discrete ) z2 = z ; } float alpha = 1; if (chanMap.count(Chan_Alpha)) { const int alphaChan = chanMap[Chan_Alpha]; alpha = ( raw || type == eAlpha ) ? pts[alphaChan] : ((pts[alphaChan] - pts2[alphaChan]) / pts[alphaChan]); } foreach(chan, channels) { if (chan == Chan_Z) pels.push_back(1/z); else if (chan == Chan_DeepFront ) pels.push_back(z); else if ( chan == Chan_DeepBack ) { pels.push_back(z2); } else if (chan == Chan_Alpha) pels.push_back(alpha); else if (chanMap.count(chan)) { if (! raw && premult && IsRGB(chan) && chanMap.count(Chan_Alpha)) { pels.push_back(pts[chanMap[chan]] * alpha); } else { pels.push_back( pts[ chanMap[chan] ] ); } } else if (IsRGBA(chan)) pels.push_back(alpha); else pels.push_back(0); } } plane.addPixel(pels); } DtexDestroyPixel(pixel); return true; } }; static DeepReader* build(DeepReaderOwner* op, const std::string& fn) { return new dtexDeepReader(op, fn); } static DeepReaderFormat* buildFormat(DeepReaderOwner* op) { return new dtexDeepReaderFormat(); } static const DeepReader::Description d("dtex\0deepshad\0dshd\0tex\0", "dtex", build, buildFormat); Lock dtexDeepReader::sLibraryLock; } }