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 "PtexTriangleFilter.h"
41 #include "PtexTriangleKernel.h"
42 #include "PtexUtils.h"
43 
44 namespace {
squared(float x)45     inline float squared(float x) { return x*x; }
46 }
47 
48 PTEX_NAMESPACE_BEGIN
49 
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)50 void PtexTriangleFilter::eval(float* result, int firstChan, int nChannels,
51                               int faceid, float u, float v,
52                               float uw1, float vw1, float uw2, float vw2,
53                               float width, float blur)
54 {
55     // init
56     if (!_tx || nChannels <= 0) return;
57     if (faceid < 0 || faceid >= _tx->numFaces()) return;
58     _ntxchan = _tx->numChannels();
59     _dt = _tx->dataType();
60     _firstChanOffset = firstChan*DataSize(_dt);
61     _nchan = PtexUtils::min(nChannels, _ntxchan-firstChan);
62 
63     // get face info
64     const FaceInfo& f = _tx->getFaceInfo(faceid);
65 
66     // if neighborhood is constant, just return constant value of face
67     if (f.isNeighborhoodConstant()) {
68         PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) );
69         if (data) {
70             char* d = (char*) data->getData() + _firstChanOffset;
71             Ptex::ConvertToFloat(result, d, _dt, _nchan);
72         }
73         return;
74     }
75 
76     // clamp u and v
77     u = PtexUtils::clamp(u, 0.0f, 1.0f);
78     v = PtexUtils::clamp(v, 0.0f, 1.0f);
79 
80     // build kernel
81     PtexTriangleKernel k;
82     buildKernel(k, u, v, uw1, vw1, uw2, vw2, width, blur, f.res);
83 
84     // accumulate the weight as we apply
85     _weight = 0;
86 
87     // allocate temporary result
88     _result = (float*) alloca(sizeof(float)*_nchan);
89     memset(_result, 0, sizeof(float)*_nchan);
90 
91     // apply to faces
92     splitAndApply(k, faceid, f);
93 
94     // normalize (both for data type and cumulative kernel weight applied)
95     // and output result
96     float scale = 1.0f / (_weight * OneValue(_dt));
97     for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale);
98 
99     // clear temp result
100     _result = 0;
101 }
102 
103 
104 
buildKernel(PtexTriangleKernel & k,float u,float v,float uw1,float vw1,float uw2,float vw2,float width,float blur,Res faceRes)105 void PtexTriangleFilter::buildKernel(PtexTriangleKernel& k, float u, float v,
106                                      float uw1, float vw1, float uw2, float vw2,
107                                      float width, float blur, Res faceRes)
108 {
109     const float sqrt3 = 1.7320508075688772f;
110 
111     // compute ellipse coefficients, A*u^2 + B*u*v + C*v^2 == AC - B^2/4
112     float scaleAC = 0.25f * width*width;
113     float scaleB = -2.0f * scaleAC;
114     float A = (vw1*vw1 + vw2*vw2) * scaleAC;
115     float B = (uw1*vw1 + uw2*vw2) * scaleB;
116     float C = (uw1*uw1 + uw2*uw2) * scaleAC;
117 
118     // convert to cartesian domain
119     float Ac = 0.75f * A;
120     float Bc = float(sqrt3/2) * (B-A);
121     float Cc = 0.25f * A - 0.5f * B + C;
122 
123     // compute min blur for eccentricity clamping
124     const float maxEcc = 15.0f; // max eccentricity
125     const float eccRatio = (maxEcc*maxEcc + 1.0f) / (maxEcc*maxEcc - 1.0f);
126     float X = sqrtf(squared(Ac - Cc) + squared(Bc));
127     float b_e = 0.5f * (eccRatio * X - (Ac + Cc));
128 
129     // compute min blur for texel clamping
130     // (ensure that ellipse is no smaller than a texel)
131     float b_t = squared(0.5f / (float)faceRes.u());
132 
133     // add blur
134     float b_b = 0.25f * blur * blur;
135     float b = PtexUtils::max(b_b, PtexUtils::max(b_e, b_t));
136     Ac += b;
137     Cc += b;
138 
139     // compute minor radius
140     float m = sqrtf(2.0f*(Ac*Cc - 0.25f*Bc*Bc) / (Ac + Cc + X));
141 
142     // choose desired resolution
143     int reslog2 = PtexUtils::max(0, PtexUtils::calcResFromWidth(2.0f*m));
144 
145     // convert back to triangular domain
146     A = float(4/3.0) * Ac;
147     B = float(2/sqrt3) * Bc + A;
148     C = -0.25f * A + 0.5f * B + Cc;
149 
150     // scale by kernel width
151     float scale = PtexTriangleKernelWidth * PtexTriangleKernelWidth;
152     A *= scale;
153     B *= scale;
154     C *= scale;
155 
156     // find u,v,w extents
157     float uw = PtexUtils::min(sqrtf(C), 1.0f);
158     float vw = PtexUtils::min(sqrtf(A), 1.0f);
159     float ww = PtexUtils::min(sqrtf(A-B+C), 1.0f);
160 
161     // init kernel
162     float w = 1.0f - u - v;
163     k.set(Res((int8_t)reslog2, (int8_t)reslog2), u, v, u-uw, v-vw, w-ww, u+uw, v+vw, w+ww, A, B, C);
164 }
165 
166 
splitAndApply(PtexTriangleKernel & k,int faceid,const Ptex::FaceInfo & f)167 void PtexTriangleFilter::splitAndApply(PtexTriangleKernel& k, int faceid, const Ptex::FaceInfo& f)
168 {
169     // do we need to split? if so, split kernel and apply across edge(s)
170     if (k.u1 < 0 && f.adjface(2) >= 0) {
171         PtexTriangleKernel ka;
172         k.splitU(ka);
173         applyAcrossEdge(ka, f, 2);
174     }
175     if (k.v1 < 0 && f.adjface(0) >= 0) {
176         PtexTriangleKernel ka;
177         k.splitV(ka);
178         applyAcrossEdge(ka, f, 0);
179     }
180     if (k.w1 < 0 && f.adjface(1) >= 0) {
181         PtexTriangleKernel ka;
182         k.splitW(ka);
183         applyAcrossEdge(ka, f, 1);
184     }
185     // apply to local face
186     apply(k, faceid, f);
187 }
188 
189 
applyAcrossEdge(PtexTriangleKernel & k,const Ptex::FaceInfo & f,int eid)190 void PtexTriangleFilter::applyAcrossEdge(PtexTriangleKernel& k,
191                                          const Ptex::FaceInfo& f, int eid)
192 {
193     int afid = f.adjface(eid), aeid = f.adjedge(eid);
194     const Ptex::FaceInfo& af = _tx->getFaceInfo(afid);
195     k.reorient(eid, aeid);
196     splitAndApply(k, afid, af);
197 }
198 
199 
apply(PtexTriangleKernel & k,int faceid,const Ptex::FaceInfo & f)200 void PtexTriangleFilter::apply(PtexTriangleKernel& k, int faceid, const Ptex::FaceInfo& f)
201 {
202     // clamp kernel face (resolution and extent)
203     k.clampRes(f.res);
204     k.clampExtent();
205 
206     // build kernel iterators
207     PtexTriangleKernelIter keven, kodd;
208     k.getIterators(keven, kodd);
209     if (!keven.valid && !kodd.valid) return;
210 
211     // get face data, and apply
212     PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) );
213     if (!dh) return;
214 
215     if (keven.valid) applyIter(keven, dh);
216     if (kodd.valid) applyIter(kodd, dh);
217 }
218 
219 
applyIter(PtexTriangleKernelIter & k,PtexFaceData * dh)220 void PtexTriangleFilter::applyIter(PtexTriangleKernelIter& k, PtexFaceData* dh)
221 {
222     if (dh->isConstant()) {
223         k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
224         _weight += k.weight;
225     }
226     else if (dh->isTiled()) {
227         Ptex::Res tileres = dh->tileRes();
228         PtexTriangleKernelIter kt = k;
229         int tileresu = tileres.u();
230         int tileresv = tileres.v();
231         kt.rowlen = tileresu;
232         int ntilesu = k.rowlen / kt.rowlen;
233         int wOffsetBase = k.rowlen - tileresu;
234         for (int tilev = k.v1 / tileresv, tilevEnd = (k.v2-1) / tileresv; tilev <= tilevEnd; tilev++) {
235             int vOffset = tilev * tileresv;
236             kt.v = k.v - (float)vOffset;
237             kt.v1 = PtexUtils::max(0, k.v1 - vOffset);
238             kt.v2 = PtexUtils::min(k.v2 - vOffset, tileresv);
239             for (int tileu = k.u1 / tileresu, tileuEnd = (k.u2-1) / tileresu; tileu <= tileuEnd; tileu++) {
240                 int uOffset = tileu * tileresu;
241                 int wOffset = wOffsetBase - uOffset - vOffset;
242                 kt.u = k.u - (float)uOffset;
243                 kt.u1 = PtexUtils::max(0, k.u1 - uOffset);
244                 kt.u2 = PtexUtils::min(k.u2 - uOffset, tileresu);
245                 kt.w1 = k.w1 - wOffset;
246                 kt.w2 = k.w2 - wOffset;
247                 PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
248                 if (th) {
249                     kt.weight = 0;
250                     if (th->isConstant())
251                         kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
252                     else
253                         kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
254                     _weight += kt.weight;
255                 }
256             }
257         }
258     }
259     else {
260         k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
261         _weight += k.weight;
262     }
263 }
264 
265 PTEX_NAMESPACE_END
266