1 //-*****************************************************************************
2 //
3 // Copyright (c) 2009-2011,
4 //  Sony Pictures Imageworks Inc. and
5 //  Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd.
6 //
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are
11 // met:
12 // *       Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // *       Redistributions in binary form must reproduce the above
15 // copyright notice, this list of conditions and the following disclaimer
16 // in the documentation and/or other materials provided with the
17 // distribution.
18 // *       Neither the name of Sony Pictures Imageworks, nor
19 // Industrial Light & Magic, nor the names of their contributors may be used
20 // to endorse or promote products derived from this software without specific
21 // prior written permission.
22 //
23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 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 DAMAGE.
34 //
35 //-*****************************************************************************
36 
37 #include "WriteGeo.h"
38 #include "ArbGeomParams.h"
39 
40 #include <ai.h>
41 #include <sstream>
42 
43 //-*****************************************************************************
44 
45 #if AI_VERSION_ARCH_NUM == 3
46     #if AI_VERSION_MAJOR_NUM < 4
47         #define AiNodeGetNodeEntry(node)   ((node)->base_node)
48     #endif
49 #endif
50 
nodeHasParameter(struct AtNode * node,const std::string & paramName)51 bool nodeHasParameter( struct AtNode * node, const std::string & paramName)
52 {
53     return AiNodeEntryLookUpParameter( AiNodeGetNodeEntry( node ),
54             paramName.c_str() ) != NULL;
55 }
56 
57 
58 //-*****************************************************************************
59 
ApplyTransformation(struct AtNode * node,MatrixSampleMap * xformSamples,ProcArgs & args)60 void ApplyTransformation( struct AtNode * node,
61         MatrixSampleMap * xformSamples, ProcArgs &args )
62 {
63     if ( !node || !xformSamples || xformSamples->empty() )
64     {
65         return;
66     }
67 
68     // confirm that this node has a parameter
69     if ( !nodeHasParameter( node, "matrix" ) )
70     {
71         return;
72     }
73 
74     // check to see that we're not a single identity matrix
75     if (xformSamples->size() == 1 &&
76             xformSamples->begin()->second == Imath::M44d())
77     {
78         return;
79     }
80 
81 
82     std::vector<float> sampleTimes;
83     sampleTimes.reserve(xformSamples->size());
84 
85     std::vector<float> mlist;
86     mlist.reserve( 16* xformSamples->size() );
87 
88     for ( MatrixSampleMap::iterator I = xformSamples->begin();
89             I != xformSamples->end(); ++I )
90     {
91         // build up a vector of relative sample times to feed to
92         // "transform_time_samples" or "time_samples"
93         sampleTimes.push_back( GetRelativeSampleTime(args, (*I).first) );
94 
95 
96         for (int i = 0; i < 16; i++)
97         {
98             mlist.push_back( (*I).second.getValue()[i] );
99         }
100     }
101 
102     AiNodeSetArray(node, "matrix",
103                 ArrayConvert(1, xformSamples->size(),
104                         AI_TYPE_MATRIX, &mlist[0]));
105 
106 
107     if ( sampleTimes.size() > 1 )
108     {
109         // persp_camera calls it time_samples while the primitives call it
110         // transform_time_samples
111         if ( nodeHasParameter( node, "transform_time_samples" ) )
112         {
113             AiNodeSetArray(node, "transform_time_samples",
114                             ArrayConvert(sampleTimes.size(), 1,
115                                     AI_TYPE_FLOAT, &sampleTimes[0]));
116         }
117         else if ( nodeHasParameter( node, "time_samples" ) )
118         {
119             AiNodeSetArray(node, "time_samples",
120                             ArrayConvert(sampleTimes.size(), 1,
121                                     AI_TYPE_FLOAT, &sampleTimes[0]));
122         }
123         else
124         {
125             //TODO, warn if neither is present? Should be there in all
126             //commercial versions of arnold by now.
127         }
128     }
129 }
130 
131 //-*****************************************************************************
132 
133 
134 template <typename geomParamT>
ProcessIndexedBuiltinParam(geomParamT param,const SampleTimeSet & sampleTimes,std::vector<float> & values,std::vector<AtUInt32> & idxs,size_t elementSize)135 void ProcessIndexedBuiltinParam(
136         geomParamT param,
137         const SampleTimeSet & sampleTimes,
138         std::vector<float> & values,
139         std::vector<AtUInt32> & idxs,
140         size_t elementSize)
141 {
142     if ( !param.valid() ) { return; }
143 
144     bool isFirstSample = true;
145     for ( SampleTimeSet::iterator I = sampleTimes.begin();
146           I != sampleTimes.end(); ++I, isFirstSample = false)
147     {
148         ISampleSelector sampleSelector( *I );
149 
150 
151         switch ( param.getScope() )
152         {
153         case kVaryingScope:
154         case kVertexScope:
155         {
156             // a value per-point, idxs should be the same as vidxs
157             // so we'll leave it empty
158 
159             // we'll get the expanded form here
160             typename geomParamT::Sample sample = param.getExpandedValue(
161                     sampleSelector);
162 
163             size_t footprint = sample.getVals()->size() * elementSize;
164 
165             values.reserve( values.size() + footprint );
166             values.insert( values.end(),
167                     (float32_t*) sample.getVals()->get(),
168                     ((float32_t*) sample.getVals()->get()) + footprint );
169 
170             break;
171         }
172         case kFacevaryingScope:
173         {
174             // get the indexed form and feed to nidxs
175 
176             typename geomParamT::Sample sample = param.getIndexedValue(
177                     sampleSelector);
178 
179             if ( isFirstSample )
180             {
181                 idxs.reserve( sample.getIndices()->size() );
182                 idxs.insert( idxs.end(),
183                         sample.getIndices()->get(),
184                         sample.getIndices()->get() +
185                                 sample.getIndices()->size() );
186             }
187 
188             size_t footprint = sample.getVals()->size() * elementSize;
189             values.reserve( values.size() + footprint );
190             values.insert( values.end(),
191                     (const float32_t*) sample.getVals()->get(),
192                     ((const float32_t*) sample.getVals()->get()) + footprint );
193 
194             break;
195         }
196         default:
197             break;
198         }
199 
200 
201     }
202 
203 
204 }
205 
206 //-*****************************************************************************
207 
208 namespace
209 {
210     // Arnold scene build is single-threaded so we don't have to lock around
211     // access to this for now.
212     typedef std::map<std::string, AtNode *> NodeCache;
213     NodeCache g_meshCache;
214 }
215 
216 
217 //-*************************************************************************
218 // This is templated to handle shared behavior of IPolyMesh and ISubD
219 
220 // We send in our empty sampleTimes and vidxs because polymesh needs those
221 // for processing animated normal.
222 
223 
224 // The return value is the polymesh node. If instanced, it will be returned
225 // for the first created instance only.
226 template <typename primT>
ProcessPolyMeshBase(primT & prim,ProcArgs & args,SampleTimeSet & sampleTimes,std::vector<AtUInt32> & vidxs,int subdiv_iterations,MatrixSampleMap * xformSamples,const std::string & facesetName="")227 AtNode * ProcessPolyMeshBase(
228         primT & prim, ProcArgs & args,
229         SampleTimeSet & sampleTimes,
230         std::vector<AtUInt32> & vidxs,
231         int subdiv_iterations,
232         MatrixSampleMap * xformSamples,
233         const std::string & facesetName = "" )
234 {
235     if ( !prim.valid() )
236     {
237         return NULL;
238     }
239 
240     typename primT::schema_type  &ps = prim.getSchema();
241     TimeSamplingPtr ts = ps.getTimeSampling();
242 
243     if ( ps.getTopologyVariance() != kHeterogenousTopology )
244     {
245         GetRelevantSampleTimes( args, ts, ps.getNumSamples(), sampleTimes );
246     }
247     else
248     {
249         sampleTimes.insert( args.frame / args.fps );
250     }
251 
252     std::string name = args.nameprefix + prim.getFullName();
253 
254     AtNode * instanceNode = NULL;
255 
256     std::string cacheId;
257 
258     if ( args.makeInstance )
259     {
260         std::ostringstream buffer;
261         AbcA::ArraySampleKey sampleKey;
262 
263 
264         for ( SampleTimeSet::iterator I = sampleTimes.begin();
265                 I != sampleTimes.end(); ++I )
266         {
267             ISampleSelector sampleSelector( *I );
268             ps.getPositionsProperty().getKey(sampleKey, sampleSelector);
269 
270             buffer << GetRelativeSampleTime( args, (*I) ) << ":";
271             sampleKey.digest.print(buffer);
272             buffer << ":";
273         }
274 
275         buffer << "@" << subdiv_iterations;
276         buffer << "@" << facesetName;
277 
278         cacheId = buffer.str();
279 
280         instanceNode = AiNode( "ginstance" );
281         AiNodeSetStr( instanceNode, "name", name.c_str() );
282         args.createdNodes.push_back(instanceNode);
283 
284         if ( args.proceduralNode )
285         {
286             AiNodeSetInt( instanceNode, "visibility",
287                     AiNodeGetInt( args.proceduralNode, "visibility" ) );
288 
289         }
290         else
291         {
292             AiNodeSetInt( instanceNode, "visibility", AI_RAY_ALL );
293         }
294 
295         ApplyTransformation( instanceNode, xformSamples, args );
296 
297 
298         NodeCache::iterator I = g_meshCache.find(cacheId);
299         if ( I != g_meshCache.end() )
300         {
301             AiNodeSetPtr(instanceNode, "node", (*I).second );
302             return NULL;
303         }
304 
305     }
306 
307 
308 
309     SampleTimeSet singleSampleTimes;
310     singleSampleTimes.insert( args.frame / args.fps );
311 
312 
313     std::vector<AtByte> nsides;
314     std::vector<float> vlist;
315 
316     std::vector<float> uvlist;
317     std::vector<AtUInt32> uvidxs;
318 
319 
320     // POTENTIAL OPTIMIZATIONS LEFT TO THE READER
321     // 1) vlist needn't be copied if it's a single sample
322 
323     bool isFirstSample = true;
324     for ( SampleTimeSet::iterator I = sampleTimes.begin();
325           I != sampleTimes.end(); ++I, isFirstSample = false)
326     {
327         ISampleSelector sampleSelector( *I );
328         typename primT::schema_type::Sample sample = ps.getValue( sampleSelector );
329 
330         if ( isFirstSample )
331         {
332             size_t numPolys = sample.getFaceCounts()->size();
333             nsides.reserve( sample.getFaceCounts()->size() );
334             for ( size_t i = 0; i < numPolys; ++i )
335             {
336                 int32_t n = sample.getFaceCounts()->get()[i];
337 
338                 if ( n > 255 )
339                 {
340                     // TODO, warning about unsupported face
341                     return NULL;
342                 }
343 
344                 nsides.push_back( (AtByte) n );
345             }
346 
347             size_t vidxSize = sample.getFaceIndices()->size();
348             vidxs.reserve( vidxSize );
349             vidxs.insert( vidxs.end(), sample.getFaceIndices()->get(),
350                     sample.getFaceIndices()->get() + vidxSize );
351         }
352 
353 
354         vlist.reserve( vlist.size() + sample.getPositions()->size() * 3);
355         vlist.insert( vlist.end(),
356                 (const float32_t*) sample.getPositions()->get(),
357                 ((const float32_t*) sample.getPositions()->get()) +
358                         sample.getPositions()->size() * 3 );
359     }
360 
361     ProcessIndexedBuiltinParam(
362             ps.getUVsParam(),
363             singleSampleTimes,
364             uvlist,
365             uvidxs,
366             2);
367 
368 
369     AtNode* meshNode = AiNode( "polymesh" );
370 
371     if (!meshNode)
372     {
373         AiMsgError("Failed to make polymesh node for %s",
374                 prim.getFullName().c_str());
375         return NULL;
376     }
377 
378     args.createdNodes.push_back(meshNode);
379 
380     if ( instanceNode != NULL)
381     {
382         AiNodeSetStr( meshNode, "name", (name + ":src").c_str() );
383     }
384     else
385     {
386         AiNodeSetStr( meshNode, "name", name.c_str() );
387     }
388 
389 
390 
391 
392     AiNodeSetArray(meshNode, "vidxs",
393             ArrayConvert(vidxs.size(), 1, AI_TYPE_UINT,
394                     (void*)&vidxs[0]));
395 
396     AiNodeSetArray(meshNode, "nsides",
397             ArrayConvert(nsides.size(), 1, AI_TYPE_BYTE,
398                     &(nsides[0])));
399 
400     AiNodeSetArray(meshNode, "vlist",
401             ArrayConvert( vlist.size() / sampleTimes.size(),
402                     sampleTimes.size(), AI_TYPE_FLOAT, (void*)(&(vlist[0]))));
403 
404     if ( !uvlist.empty() )
405     {
406         //TODO, option to disable v flipping
407         for (size_t i = 1, e = uvlist.size(); i < e; i += 2)
408         {
409             uvlist[i] = 1.0 - uvlist[i];
410         }
411 
412         AiNodeSetArray(meshNode, "uvlist",
413             ArrayConvert( uvlist.size(), 1, AI_TYPE_FLOAT,
414                 (void*)(&(uvlist[0]))));
415 
416         if ( !uvidxs.empty() )
417         {
418             AiNodeSetArray(meshNode, "uvidxs",
419                     ArrayConvert(uvidxs.size(), 1, AI_TYPE_UINT,
420                             &(uvidxs[0])));
421         }
422         else
423         {
424             AiNodeSetArray(meshNode, "uvidxs",
425                     ArrayConvert(vidxs.size(), 1, AI_TYPE_UINT,
426                             &(vidxs[0])));
427         }
428     }
429 
430     if ( sampleTimes.size() > 1 )
431     {
432         std::vector<float> relativeSampleTimes;
433         relativeSampleTimes.reserve( sampleTimes.size() );
434 
435         for (SampleTimeSet::const_iterator I = sampleTimes.begin();
436                 I != sampleTimes.end(); ++I )
437         {
438             relativeSampleTimes.push_back(
439                     GetRelativeSampleTime( args, (*I) ) );
440 
441         }
442 
443         AiNodeSetArray( meshNode, "deform_time_samples",
444                 ArrayConvert(relativeSampleTimes.size(), 1,
445                         AI_TYPE_FLOAT, &relativeSampleTimes[0]));
446     }
447 
448     // faceset visibility array
449     if ( !facesetName.empty() )
450     {
451         if ( ps.hasFaceSet( facesetName ) )
452         {
453             ISampleSelector frameSelector( *singleSampleTimes.begin() );
454 
455 
456             IFaceSet faceSet = ps.getFaceSet( facesetName );
457             IFaceSetSchema::Sample faceSetSample =
458                     faceSet.getSchema().getValue( frameSelector );
459 
460             std::set<int> facesToKeep;
461 
462 
463             facesToKeep.insert( faceSetSample.getFaces()->get(),
464                     faceSetSample.getFaces()->get() +
465                             faceSetSample.getFaces()->size() );
466 
467             bool *faceVisArray = new bool(nsides.size());
468 
469             for ( int i = 0; i < (int) nsides.size(); ++i )
470             {
471                 faceVisArray[i] = facesToKeep.find( i ) != facesToKeep.end();
472             }
473 
474             if ( AiNodeDeclare( meshNode, "face_visibility", "uniform BOOL" ) )
475             {
476                 AiNodeSetArray( meshNode, "face_visibility",
477                         ArrayConvert( nsides.size(), 1, AI_TYPE_BOOLEAN,
478                                 faceVisArray ) );
479             }
480 
481             delete[] faceVisArray;
482         }
483     }
484 
485     {
486         ICompoundProperty arbGeomParams = ps.getArbGeomParams();
487         ISampleSelector frameSelector( *singleSampleTimes.begin() );
488 
489         AddArbitraryGeomParams( arbGeomParams, frameSelector, meshNode );
490     }
491 
492 
493     if ( instanceNode == NULL )
494     {
495         if ( xformSamples )
496         {
497             ApplyTransformation( meshNode, xformSamples, args );
498         }
499 
500         return meshNode;
501     }
502     else
503     {
504         AiNodeSetInt( meshNode, "visibility", 0 );
505 
506         AiNodeSetPtr(instanceNode, "node", meshNode );
507         g_meshCache[cacheId] = meshNode;
508         return meshNode;
509 
510     }
511 
512 }
513 
514 //-*************************************************************************
515 
ProcessPolyMesh(IPolyMesh & polymesh,ProcArgs & args,MatrixSampleMap * xformSamples,const std::string & facesetName)516 void ProcessPolyMesh( IPolyMesh &polymesh, ProcArgs &args,
517         MatrixSampleMap * xformSamples, const std::string & facesetName )
518 {
519     SampleTimeSet sampleTimes;
520     std::vector<AtUInt32> vidxs;
521 
522     AtNode * meshNode = ProcessPolyMeshBase(
523             polymesh, args, sampleTimes, vidxs, 0, xformSamples,
524                     facesetName );
525 
526     // This is a valid condition for the second instance onward and just
527     // means that we don't need to do anything further.
528     if ( !meshNode )
529     {
530         return;
531     }
532 
533     IPolyMeshSchema &ps = polymesh.getSchema();
534 
535     std::vector<float> nlist;
536     std::vector<AtUInt32> nidxs;
537 
538     ProcessIndexedBuiltinParam(
539             ps.getNormalsParam(),
540             sampleTimes,
541             nlist,
542             nidxs,
543             3);
544 
545     if ( !nlist.empty() )
546     {
547         AiNodeSetArray(meshNode, "nlist",
548             ArrayConvert( nlist.size() / sampleTimes.size(),
549                     sampleTimes.size(), AI_TYPE_FLOAT, (void*)(&(nlist[0]))));
550 
551         if ( !nidxs.empty() )
552         {
553             AiNodeSetArray(meshNode, "nidxs",
554                     ArrayConvert(nidxs.size(), 1, AI_TYPE_UINT,
555                             &(nidxs[0])));
556         }
557         else
558         {
559             AiNodeSetArray(meshNode, "nidxs",
560                     ArrayConvert(vidxs.size(), 1, AI_TYPE_UINT,
561                             &(vidxs[0])));
562         }
563     }
564 
565 }
566 
567 //-*************************************************************************
568 
ProcessSubD(ISubD & subd,ProcArgs & args,MatrixSampleMap * xformSamples,const std::string & facesetName)569 void ProcessSubD( ISubD &subd, ProcArgs &args,
570         MatrixSampleMap * xformSamples, const std::string & facesetName )
571 {
572     SampleTimeSet sampleTimes;
573     std::vector<AtUInt32> vidxs;
574 
575     AtNode * meshNode = ProcessPolyMeshBase(
576             subd, args, sampleTimes, vidxs, args.subdIterations,
577                     xformSamples, facesetName );
578 
579     // This is a valid condition for the second instance onward and just
580     // means that we don't need to do anything further.
581     if ( !meshNode )
582     {
583         return;
584     }
585 
586 
587     AiNodeSetStr( meshNode, "subdiv_type", "catclark" );
588 }
589 
590