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 
45 PTEX_NAMESPACE_BEGIN
46 
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)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 = 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
95     PtexSeparableKernel k;
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);
115     assert(k.uw <= PtexSeparableKernel::kmax && k.vw <= PtexSeparableKernel::kmax);
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 
splitAndApply(PtexSeparableKernel & k,int faceid,const Ptex::FaceInfo & f)135 void PtexSeparableFilter::splitAndApply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
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 
applyAcrossEdge(PtexSeparableKernel & k,int faceid,const Ptex::FaceInfo & f,int eid)215 void PtexSeparableFilter::applyAcrossEdge(PtexSeparableKernel& k,
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 
applyToCorner(PtexSeparableKernel & k,int faceid,const Ptex::FaceInfo & f,int eid)257 void PtexSeparableFilter::applyToCorner(PtexSeparableKernel& k, int faceid,
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 
applyToCornerFace(PtexSeparableKernel & k,const Ptex::FaceInfo & f,int eid,int cfid,const Ptex::FaceInfo & cf,int ceid)331 void PtexSeparableFilter::applyToCornerFace(PtexSeparableKernel& k, const Ptex::FaceInfo& f, int eid,
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 
apply(PtexSeparableKernel & k,int faceid,const Ptex::FaceInfo & f)348 void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
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();
375         PtexSeparableKernel kt;
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 
428 PTEX_NAMESPACE_END
429