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