Ptex
PtexSeparableFilter.cpp
Go to the documentation of this file.
1/*
2PTEX SOFTWARE
3Copyright 2014 Disney Enterprises, Inc. All rights reserved
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the
15 distribution.
16
17 * The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
18 Studios" or the names of its contributors may NOT be used to
19 endorse or promote products derived from this software without
20 specific prior written permission from Walt Disney Pictures.
21
22Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
23CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
24BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
25FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
26IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
27CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
31THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
34*/
35
36#include "PtexPlatform.h"
37#include <cmath>
38#include <assert.h>
39
40#include "PtexSeparableFilter.h"
41#include "PtexSeparableKernel.h"
42#include "PtexUtils.h"
43
44
46
47void PtexSeparableFilter::eval(float* result, int firstChan, int nChannels,
48 int faceid, float u, float v,
49 float uw1, float vw1, float uw2, float vw2,
50 float width, float blur)
51{
52 // init
53 if (!_tx || nChannels <= 0) return;
54 if (faceid < 0 || faceid >= _tx->numFaces()) return;
55 _firstChanOffset = firstChan*DataSize(_dt);
56 _nchan = PtexUtils::min(nChannels, _ntxchan-firstChan);
57
58 // get face info
59 const FaceInfo& f = _tx->getFaceInfo(faceid);
60
61 // if neighborhood is constant, just return constant value of face
62 if (f.isNeighborhoodConstant()) {
63 PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) );
64 if (data) {
65 char* d = (char*) data->getData() + _firstChanOffset;
66 Ptex::ConvertToFloat(result, d, _dt, _nchan);
67 }
68 return;
69 }
70
71 // find filter width as bounding box of vectors w1 and w2
72 float uw = PtexUtils::abs(uw1) + PtexUtils::abs(uw2), vw = PtexUtils::abs(vw1) + PtexUtils::abs(vw2);
73
74 // handle border modes
75 bool return_black = false;
76
77 switch (_uMode) {
78 case m_clamp: u = PtexUtils::clamp(u, 0.0f, 1.0f); break;
79 case m_periodic: u = u-PtexUtils::floor(u); break;
80 case m_black: if (u <= -1.0f || u >= 2.0f) return_black = true; break;
81 }
82
83 switch (_vMode) {
84 case m_clamp: v = PtexUtils::clamp(v, 0.0f, 1.0f); break;
85 case m_periodic: v = v-PtexUtils::floor(v); break;
86 case m_black: if (v <= -1.0f || v >= 2.0f) return_black = true; break;
87 }
88
89 if (return_black) {
90 memset(result, 0, sizeof(float)*_nchan);
91 return;
92 }
93
94 // build kernel
96 if (f.isSubface()) {
97 // for a subface, build the kernel as if it were on a main face and then downres
98 uw = uw * width + blur * 2.0f;
99 vw = vw * width + blur * 2.0f;
100 buildKernel(k, u*.5f, v*.5f, uw*.5f, vw*.5f,
101 Ptex::Res((int8_t)(f.res.ulog2+1),(int8_t)(f.res.vlog2+1)));
102 if (k.res.ulog2 == 0) k.upresU();
103 if (k.res.vlog2 == 0) k.upresV();
104 k.res.ulog2--; k.res.vlog2--;
105 }
106 else {
107 uw = uw * width + blur;
108 vw = vw * width + blur;
109 buildKernel(k, u, v, uw, vw, f.res);
110 }
111 k.stripZeros();
112
113 // check kernel (debug only)
114 assert(k.uw > 0 && k.vw > 0);
116 _weight = k.weight();
117
118 // allocate temporary result
119 _result = (float*) alloca(sizeof(float)*_nchan);
120 memset(_result, 0, sizeof(float)*_nchan);
121
122 // apply to faces
123 splitAndApply(k, faceid, f);
124
125 // normalize (both for data type and cumulative kernel weight applied)
126 // and output result
127 float scale = 1.0f / (_weight * OneValue(_dt));
128 for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale);
129
130 // clear temp result
131 _result = 0;
132}
133
134
136{
137 // do we need to split? (i.e. does kernel span an edge?)
138 bool splitR = (k.u+k.uw > k.res.u()), splitL = (k.u < 0);
139 bool splitT = (k.v+k.vw > k.res.v()), splitB = (k.v < 0);
140
141 if (_options.noedgeblend) {
142 if (splitR) k.mergeR(_uMode);
143 if (splitL) k.mergeL(_uMode);
144 if (splitT) k.mergeT(_vMode);
145 if (splitB) k.mergeB(_vMode);
146 apply(k, faceid, f);
147 return;
148 }
149
150 if (splitR || splitL || splitT || splitB) {
151 PtexSeparableKernel ka, kc;
152 if (splitR) {
153 if (f.adjface(e_right) >= 0) {
154 k.splitR(ka);
155 if (splitT) {
156 if (f.adjface(e_top) >= 0) {
157 ka.splitT(kc);
158 applyToCorner(kc, faceid, f, e_top);
159 }
160 else ka.mergeT(_vMode);
161 }
162 if (splitB) {
163 if (f.adjface(e_bottom) >= 0) {
164 ka.splitB(kc);
165 applyToCorner(kc, faceid, f, e_right);
166 }
167 else ka.mergeB(_vMode);
168 }
169 applyAcrossEdge(ka, faceid, f, e_right);
170 }
171 else k.mergeR(_uMode);
172 }
173 if (splitL) {
174 if (f.adjface(e_left) >= 0) {
175 k.splitL(ka);
176 if (splitT) {
177 if (f.adjface(e_top) >= 0) {
178 ka.splitT(kc);
179 applyToCorner(kc, faceid, f, e_left);
180 }
181 else ka.mergeT(_vMode);
182 }
183 if (splitB) {
184 if (f.adjface(e_bottom) >= 0) {
185 ka.splitB(kc);
186 applyToCorner(kc, faceid, f, e_bottom);
187 }
188 else ka.mergeB(_vMode);
189 }
190 applyAcrossEdge(ka, faceid, f, e_left);
191 }
192 else k.mergeL(_uMode);
193 }
194 if (splitT) {
195 if (f.adjface(e_top) >= 0) {
196 k.splitT(ka);
197 applyAcrossEdge(ka, faceid, f, e_top);
198 }
199 else k.mergeT(_vMode);
200 }
201 if (splitB) {
202 if (f.adjface(e_bottom) >= 0) {
203 k.splitB(ka);
204 applyAcrossEdge(ka, faceid, f, e_bottom);
205 }
206 else k.mergeB(_vMode);
207 }
208 }
209
210 // do local face
211 apply(k, faceid, f);
212}
213
214
216 int faceid, const Ptex::FaceInfo& f, int eid)
217{
218 int afid = f.adjface(eid), aeid = f.adjedge(eid);
219 const Ptex::FaceInfo* af = &_tx->getFaceInfo(afid);
220 int rot = eid - aeid + 2;
221
222 // adjust uv coord and res for face/subface boundary
223 bool fIsSubface = f.isSubface(), afIsSubface = af->isSubface();
224 if (fIsSubface != afIsSubface) {
225 if (afIsSubface) {
226 // main face to subface transition
227 // adjust res and offset uv coord for primary subface
228 bool primary = k.adjustMainToSubface(eid);
229 if (!primary) {
230 // advance ajacent face and edge id to secondary subface
231 int neid = (aeid + 3) % 4;
232 afid = af->adjface(neid);
233 aeid = af->adjedge(neid);
234 af = &_tx->getFaceInfo(afid);
235 rot += neid - aeid + 2;
236 }
237 }
238 else {
239 // subface to main face transition
240 // Note: the transform depends on which subface the kernel is
241 // coming from. The "primary" subface is the one the main
242 // face is pointing at. The secondary subface adjustment
243 // happens to be the same as for the primary subface for the
244 // next edge, so the cases can be combined.
245 bool primary = (af->adjface(aeid) == faceid);
246 k.adjustSubfaceToMain(eid - primary);
247 }
248 }
249
250 // rotate and apply (resplit if going to a subface)
251 k.rotate(rot);
252 if (afIsSubface) splitAndApply(k, afid, *af);
253 else apply(k, afid, *af);
254}
255
256
258 const Ptex::FaceInfo& f, int eid)
259{
260 // traverse clockwise around corner vertex and gather corner faces
261 int afid = faceid, aeid = eid;
262 const FaceInfo* af = &f;
263 bool prevIsSubface = af->isSubface();
264
265 const int MaxValence = 10;
266 int cfaceId[MaxValence];
267 int cedgeId[MaxValence];
268 const FaceInfo* cface[MaxValence];
269
270 int numCorners = 0;
271 for (int i = 0; i < MaxValence; i++) {
272 // advance to next face
273 int prevFace = afid;
274 afid = af->adjface(aeid);
275 aeid = (af->adjedge(aeid) + 1) % 4;
276
277 // we hit a boundary or reached starting face
278 // note: we need to check edge id too because we might have
279 // a periodic texture (w/ toroidal topology) where all 4 corners
280 // are from the same face
281 if (afid < 0 || (afid == faceid && aeid == eid)) {
282 numCorners = i - 2;
283 break;
284 }
285
286 // record face info
287 af = &_tx->getFaceInfo(afid);
288 cfaceId[i] = afid;
289 cedgeId[i] = aeid;
290 cface[i] = af;
291
292 // check to see if corner is a subface "tee"
293 bool isSubface = af->isSubface();
294 if (prevIsSubface && !isSubface && af->adjface((aeid+3)%4) == prevFace)
295 {
296 // adjust the eid depending on whether we started from
297 // the primary or secondary subface.
298 bool primary = (i==1);
299 k.adjustSubfaceToMain(eid + primary * 2);
300 k.rotate(eid - aeid + 3 - primary);
301 splitAndApply(k, afid, *af);
302 return;
303 }
304 prevIsSubface = isSubface;
305 }
306
307 if (numCorners == 1) {
308 // regular case (valence 4)
309 applyToCornerFace(k, f, eid, cfaceId[1], *cface[1], cedgeId[1]);
310 }
311 else if (numCorners > 1) {
312 // valence 5+, make kernel symmetric and apply equally to each face
313 // first, rotate to standard orientation, u=v=0
314 k.rotate(eid + 2);
315 float initialWeight = k.weight();
316 float newWeight = k.makeSymmetric(initialWeight);
317 for (int i = 1; i <= numCorners; i++) {
318 PtexSeparableKernel kc = k;
319 applyToCornerFace(kc, f, 2, cfaceId[i], *cface[i], cedgeId[i]);
320 }
321 // adjust weight for symmetrification and for additional corners
322 _weight += newWeight * (float)numCorners - initialWeight;
323 }
324 else {
325 // valence 2 or 3, ignore corner face (just adjust weight)
326 _weight -= k.weight();
327 }
328}
329
330
332 int cfid, const Ptex::FaceInfo& cf, int ceid)
333{
334 // adjust uv coord and res for face/subface boundary
335 bool fIsSubface = f.isSubface(), cfIsSubface = cf.isSubface();
336 if (fIsSubface != cfIsSubface) {
337 if (cfIsSubface) k.adjustMainToSubface(eid + 3);
338 else k.adjustSubfaceToMain(eid + 3);
339 }
340
341 // rotate and apply (resplit if going to a subface)
342 k.rotate(eid - ceid + 2);
343 if (cfIsSubface) splitAndApply(k, cfid, cf);
344 else apply(k, cfid, cf);
345}
346
347
349{
350 assert(k.u >= 0 && k.u + k.uw <= k.res.u());
351 assert(k.v >= 0 && k.v + k.vw <= k.res.v());
352
353 if (k.uw <= 0 || k.vw <= 0) return;
354
355 // downres kernel if needed
356 while (k.res.u() > f.res.u()) k.downresU();
357 while (k.res.v() > f.res.v()) k.downresV();
358
359 // get face data, and apply
360 PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) );
361 if (!dh) return;
362
363 if (dh->isConstant()) {
364 k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
365 return;
366 }
367
368 // allocate temporary result for tanvec mode (if needed)
369 bool tanvecMode = (_efm == efm_tanvec) && (_nchan >= 2) && (k.rot > 0);
370 float* result = tanvecMode ? (float*) alloca(sizeof(float)*_nchan) : _result;
371 if (tanvecMode) memset(result, 0, sizeof(float)*_nchan);
372
373 if (dh->isTiled()) {
374 Ptex::Res tileres = dh->tileRes();
376 kt.res = tileres;
377 int tileresu = tileres.u();
378 int tileresv = tileres.v();
379 int ntilesu = k.res.u() / tileresu;
380 for (int v = k.v, vw = k.vw; vw > 0; vw -= kt.vw, v += kt.vw) {
381 int tilev = v / tileresv;
382 kt.v = v % tileresv;
383 kt.vw = PtexUtils::min(vw, tileresv - kt.v);
384 kt.kv = k.kv + v - k.v;
385 for (int u = k.u, uw = k.uw; uw > 0; uw -= kt.uw, u += kt.uw) {
386 int tileu = u / tileresu;
387 kt.u = u % tileresu;
388 kt.uw = PtexUtils::min(uw, tileresu - kt.u);
389 kt.ku = k.ku + u - k.u;
390 PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
391 if (th) {
392 if (th->isConstant())
393 kt.applyConst(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
394 else
395 kt.apply(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
396 }
397 }
398 }
399 }
400 else {
401 k.apply(result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
402 }
403
404 if (tanvecMode) {
405 // rotate tangent-space vector data and update main result
406 switch (k.rot) {
407 case 0: // rot==0 included for completeness, but tanvecMode should be false in this case
408 _result[0] += result[0];
409 _result[1] += result[1];
410 break;
411 case 1:
412 _result[0] -= result[1];
413 _result[1] += result[0];
414 break;
415 case 2:
416 _result[0] -= result[0];
417 _result[1] -= result[1];
418 break;
419 case 3:
420 _result[0] += result[1];
421 _result[1] -= result[0];
422 break;
423 }
424 for (int i = 2; i < _nchan; i++) _result[i] += result[i];
425 }
426}
427
Platform-specific classes, functions, and includes.
#define PTEX_NAMESPACE_END
Definition PtexVersion.h:62
Smart-pointer for acquiring and releasing API objects.
Definition Ptexture.h:1032
void splitAndApply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
void applyToCornerFace(PtexSeparableKernel &k, const Ptex::FaceInfo &f, int eid, int cfaceid, const Ptex::FaceInfo &cf, int ceid)
virtual void 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)
Apply filter to a ptex data file.
void apply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
void applyAcrossEdge(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
void applyToCorner(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)=0
void mergeT(BorderMode mode)
void mergeB(BorderMode mode)
void apply(float *dst, void *data, DataType dt, int nChan, int nTxChan)
float makeSymmetric(float initialWeight)
void splitT(PtexSeparableKernel &k)
void adjustSubfaceToMain(int eid)
void splitB(PtexSeparableKernel &k)
void splitL(PtexSeparableKernel &k)
void mergeR(BorderMode mode)
void applyConst(float *dst, void *data, DataType dt, int nChan)
bool adjustMainToSubface(int eid)
void mergeL(BorderMode mode)
void splitR(PtexSeparableKernel &k)
virtual void getData(int faceid, void *buffer, int stride)=0
Access texture data for a face at highest-resolution.
virtual int numFaces()=0
Number of faces stored in file.
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)=0
Access resolution and adjacency information about a face.
T clamp(T x, T lo, T hi)
Definition PtexUtils.h:154
T abs(T x)
Definition PtexUtils.h:134
T min(T a, T b)
Definition PtexUtils.h:148
void ConvertToFloat(float *dst, const void *src, Ptex::DataType dt, int numChannels)
Convert a number of data values from the given data type to float.
bool noedgeblend
Disable cross-face filtering. Useful for debugging or rendering on polys.
Definition Ptexture.h:961
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:229
bool isSubface() const
Determine if face is a subface (by checking a flag).
Definition Ptexture.h:271
Res res
Resolution of face.
Definition Ptexture.h:230
EdgeId adjedge(int eid) const
Access an adjacent edge id. The eid value must be 0..3.
Definition Ptexture.h:256
int adjface(int eid) const
Access an adjacent face id. The eid value must be 0..3.
Definition Ptexture.h:259
Pixel resolution of a given texture.
Definition Ptexture.h:159
int v() const
V resolution in texels.
Definition Ptexture.h:176
int u() const
U resolution in texels.
Definition Ptexture.h:173