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