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
45 PTEX_NAMESPACE_BEGIN
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 = 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
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
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
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
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
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
PtexPlatform.h
Platform-specific classes, functions, and includes.
PtexSeparableFilter.h
PtexSeparableKernel.h
PtexUtils.h
PTEX_NAMESPACE_END
#define PTEX_NAMESPACE_END
Definition PtexVersion.h:62
PtexPtr
Smart-pointer for acquiring and releasing API objects.
Definition Ptexture.h:1032
PtexSeparableFilter::splitAndApply
void splitAndApply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
Definition PtexSeparableFilter.cpp:135
PtexSeparableFilter::applyToCornerFace
void applyToCornerFace(PtexSeparableKernel &k, const Ptex::FaceInfo &f, int eid, int cfaceid, const Ptex::FaceInfo &cf, int ceid)
Definition PtexSeparableFilter.cpp:331
PtexSeparableFilter::eval
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.
Definition PtexSeparableFilter.cpp:47
PtexSeparableFilter::apply
void apply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
Definition PtexSeparableFilter.cpp:348
PtexSeparableFilter::applyAcrossEdge
void applyAcrossEdge(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
Definition PtexSeparableFilter.cpp:215
PtexSeparableFilter::_result
float * _result
Definition PtexSeparableFilter.h:80
PtexSeparableFilter::_tx
PtexTexture * _tx
Definition PtexSeparableFilter.h:78
PtexSeparableFilter::_options
Options _options
Definition PtexSeparableFilter.h:79
PtexSeparableFilter::_uMode
BorderMode _uMode
Definition PtexSeparableFilter.h:86
PtexSeparableFilter::applyToCorner
void applyToCorner(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
Definition PtexSeparableFilter.cpp:257
PtexSeparableFilter::_ntxchan
int _ntxchan
Definition PtexSeparableFilter.h:84
PtexSeparableFilter::_weight
float _weight
Definition PtexSeparableFilter.h:81
PtexSeparableFilter::_firstChanOffset
int _firstChanOffset
Definition PtexSeparableFilter.h:82
PtexSeparableFilter::_efm
EdgeFilterMode _efm
Definition PtexSeparableFilter.h:87
PtexSeparableFilter::_dt
DataType _dt
Definition PtexSeparableFilter.h:85
PtexSeparableFilter::_vMode
BorderMode _vMode
Definition PtexSeparableFilter.h:86
PtexSeparableFilter::buildKernel
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)=0
PtexSeparableFilter::_nchan
int _nchan
Definition PtexSeparableFilter.h:83
PtexSeparableKernel
Definition PtexSeparableKernel.h:48
PtexSeparableKernel::mergeT
void mergeT(BorderMode mode)
Definition PtexSeparableKernel.h:139
PtexSeparableKernel::mergeB
void mergeB(BorderMode mode)
Definition PtexSeparableKernel.h:129
PtexSeparableKernel::stripZeros
void stripZeros()
Definition PtexSeparableKernel.h:96
PtexSeparableKernel::apply
void apply(float *dst, void *data, DataType dt, int nChan, int nTxChan)
Definition PtexSeparableKernel.h:457
PtexSeparableKernel::makeSymmetric
float makeSymmetric(float initialWeight)
Definition PtexSeparableKernel.h:397
PtexSeparableKernel::u
int u
Definition PtexSeparableKernel.h:51
PtexSeparableKernel::kv
float * kv
Definition PtexSeparableKernel.h:54
PtexSeparableKernel::splitT
void splitT(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:214
PtexSeparableKernel::rot
int rot
Definition PtexSeparableKernel.h:58
PtexSeparableKernel::res
Res res
Definition PtexSeparableKernel.h:50
PtexSeparableKernel::adjustSubfaceToMain
void adjustSubfaceToMain(int eid)
Definition PtexSeparableKernel.h:302
PtexSeparableKernel::splitB
void splitB(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:192
PtexSeparableKernel::vw
int vw
Definition PtexSeparableKernel.h:52
PtexSeparableKernel::v
int v
Definition PtexSeparableKernel.h:51
PtexSeparableKernel::splitL
void splitL(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:148
PtexSeparableKernel::downresV
void downresV()
Definition PtexSeparableKernel.h:342
PtexSeparableKernel::ku
float * ku
Definition PtexSeparableKernel.h:53
PtexSeparableKernel::uw
int uw
Definition PtexSeparableKernel.h:52
PtexSeparableKernel::mergeR
void mergeR(BorderMode mode)
Definition PtexSeparableKernel.h:120
PtexSeparableKernel::applyConst
void applyConst(float *dst, void *data, DataType dt, int nChan)
Definition PtexSeparableKernel.h:464
PtexSeparableKernel::adjustMainToSubface
bool adjustMainToSubface(int eid)
Definition PtexSeparableKernel.h:266
PtexSeparableKernel::mergeL
void mergeL(BorderMode mode)
Definition PtexSeparableKernel.h:110
PtexSeparableKernel::upresV
void upresV()
Definition PtexSeparableKernel.h:384
PtexSeparableKernel::upresU
void upresU()
Definition PtexSeparableKernel.h:371
PtexSeparableKernel::splitR
void splitR(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:171
PtexSeparableKernel::kmax
static const int kmax
Definition PtexSeparableKernel.h:55
PtexSeparableKernel::rotate
void rotate(int rotVal)
Definition PtexSeparableKernel.h:254
PtexSeparableKernel::weight
float weight() const
Definition PtexSeparableKernel.h:105
PtexSeparableKernel::downresU
void downresU()
Definition PtexSeparableKernel.h:313
PtexTexture::getData
virtual void getData(int faceid, void *buffer, int stride)=0
Access texture data for a face at highest-resolution.
PtexTexture::numFaces
virtual int numFaces()=0
Number of faces stored in file.
PtexTexture::getFaceInfo
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)=0
Access resolution and adjacency information about a face.
PTEX_NAMESPACE_BEGIN
Definition PtexSeparableKernel.cpp:42
PtexUtils::clamp
T clamp(T x, T lo, T hi)
Definition PtexUtils.h:154
PtexUtils::abs
T abs(T x)
Definition PtexUtils.h:134
PtexUtils::min
T min(T a, T b)
Definition PtexUtils.h:148
Ptex::ConvertToFloat
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.
PtexFilter::Options::noedgeblend
bool noedgeblend
Disable cross-face filtering. Useful for debugging or rendering on polys.
Definition Ptexture.h:961
Ptex::FaceInfo
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:229
Ptex::FaceInfo::isSubface
bool isSubface() const
Determine if face is a subface (by checking a flag).
Definition Ptexture.h:271
Ptex::FaceInfo::res
Res res
Resolution of face.
Definition Ptexture.h:230
Ptex::FaceInfo::adjedge
EdgeId adjedge(int eid) const
Access an adjacent edge id. The eid value must be 0..3.
Definition Ptexture.h:256
Ptex::FaceInfo::adjface
int adjface(int eid) const
Access an adjacent face id. The eid value must be 0..3.
Definition Ptexture.h:259
Ptex::Res
Pixel resolution of a given texture.
Definition Ptexture.h:159
Ptex::Res::v
int v() const
V resolution in texels.
Definition Ptexture.h:176
Ptex::Res::u
int u() const
U resolution in texels.
Definition Ptexture.h:173