1 /*
2 PARTIO SOFTWARE
3 Copyright 2010 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 "PartioEndian.h"
37 #include "../core/ParticleHeaders.h"
38 #include "io.h"
39 
40 namespace Partio
41 {
42 
43 using namespace std;
44 
writeHoudiniStr(ostream & ostream,const string & s)45 void writeHoudiniStr(ostream& ostream,const string& s)
46 {
47     write<BIGEND>(ostream,(short)s.size());
48     ostream.write(s.c_str(),s.size());
49 }
50 
51 template<class T>
52 struct Helper
53 {
54     T addAttribute(ParticlesDataMutable* simple, const char* name, ParticleAttributeType type, int size);
55     int registerIndexedStr(const T& attribute,const char* str);
56 };
57 template<>
58 struct Helper<ParticleAttribute>
59 {
addAttributePartio::Helper60     ParticleAttribute addAttribute(ParticlesDataMutable* simple, const char* name, ParticleAttributeType type, int size) {return simple->addAttribute(name,type,size);}
registerIndexedStrPartio::Helper61     int registerIndexedStr(ParticlesDataMutable* simple, const ParticleAttribute& attribute,const char* str) {return simple->registerIndexedStr(attribute,str);}
62 };
63 template<>
64 struct Helper<FixedAttribute>
65 {
addAttributePartio::Helper66     FixedAttribute addAttribute(ParticlesDataMutable* simple, const char* name, ParticleAttributeType type, int size) {return simple->addFixedAttribute(name,type,size);}
registerIndexedStrPartio::Helper67     int registerIndexedStr(ParticlesDataMutable* simple, const FixedAttribute& attribute,const char* str) {return simple->registerFixedIndexedStr(attribute,str);}
68 };
69 
70 class DummyAttribute {};
71 template<>
72 struct Helper<DummyAttribute>
73 {
addAttributePartio::Helper74     DummyAttribute addAttribute(ParticlesDataMutable* simple, const char* name, ParticleAttributeType type, int size) { return DummyAttribute(); }
registerIndexedStrPartio::Helper75     int registerIndexedStr(ParticlesDataMutable* simple, const DummyAttribute& attribute,const char* str) { return 0; }
76 };
77 struct DummyAccessor{
78     template<class T>
DummyAccessorPartio::DummyAccessor79     DummyAccessor(const T& /*attr*/){}
80 };
81 
82 template<class TAttribute, class TAccessor>
getAttributes(int & particleSize,vector<int> & attrOffsets,vector<TAttribute> & attrHandles,vector<TAccessor> & accessors,int nAttrib,istream * input,ParticlesDataMutable * simple,bool headersOnly,std::ostream * errorStream)83 bool getAttributes(int& particleSize, vector<int>& attrOffsets, vector<TAttribute>& attrHandles, vector<TAccessor>& accessors, int nAttrib, istream* input, ParticlesDataMutable* simple, bool headersOnly, std::ostream* errorStream)
84 {
85     Helper<TAttribute> helper;
86     for(int i=0;i<nAttrib;i++){
87         unsigned short nameLength;
88         read<BIGEND>(*input,nameLength);
89         char* name=new char[nameLength+1];
90         input->read(name,nameLength);name[nameLength]=0;
91         unsigned short size;
92         int houdiniType;
93         read<BIGEND>(*input,size,houdiniType);
94         if(houdiniType==0 || houdiniType==1 || houdiniType==5){
95             // read default values. don't do anything with them
96             for(int i=0;i<size;i++) {
97                 int defaultValue;
98                 input->read((char*)&defaultValue,sizeof(int));
99             }
100             ParticleAttributeType type=NONE;
101             if(houdiniType==0) type=FLOAT;
102             else if(houdiniType==1) type=INT;
103             else if(houdiniType==5) type=VECTOR;
104             attrHandles.push_back(helper.addAttribute(simple,name,type,size));
105             accessors.push_back(TAccessor(attrHandles.back()));
106             attrOffsets.push_back(particleSize);
107             particleSize+=size;
108         }else if(houdiniType==4){
109             TAttribute attribute=helper.addAttribute(simple,name,INDEXEDSTR,size);
110             attrHandles.push_back(attribute);
111             accessors.push_back(TAccessor(attrHandles.back()));
112             attrOffsets.push_back(particleSize);
113             int numIndices=0;
114             read<BIGEND>(*input,numIndices);
115             for(int ii=0;ii<numIndices;ii++){
116                 unsigned short indexNameLength;
117                 read<BIGEND>(*input,indexNameLength);
118                 char* indexName=new char[indexNameLength+1];;
119                 input->read(indexName,indexNameLength);
120                 indexName[indexNameLength]=0;
121                 if (!headersOnly) {
122                     int id=helper.registerIndexedStr(simple,attribute,indexName);
123                     if(id != ii){
124                         if(errorStream) *errorStream <<"Partio: error on read, expected registerIndexStr to return index "<<ii<<" but got "<<id<<" for string "<<indexName<<std::endl;
125                     }
126                 }
127                 delete [] indexName;
128             }
129             particleSize+=size;
130         }else if(houdiniType==2){
131             if(errorStream) *errorStream <<"Partio: found attr of type 'string', aborting"<<endl;
132             delete [] name;
133             simple->release();
134             return 0;
135         }else{
136             if(errorStream) *errorStream <<"Partio: unknown attribute "<<houdiniType<<" type... aborting"<<endl;
137             delete [] name;
138             simple->release();
139             return 0;
140         }
141         delete[] name;
142     }
143 
144     return true;
145 }
146 
147 // read buffer, seekg doesn't work with gzip
skip(istream * input,size_t numChars)148 void skip(istream *input, size_t numChars)
149 {
150     static const size_t bufferSize = 4096;
151     static char buffer[bufferSize];
152     while (numChars>0) {
153         int toRead=std::min(numChars,bufferSize);
154         input->read(buffer,toRead);
155         numChars-=toRead;
156     }
157 }
158 
159 // ignore primitive attributes, only know about Particle Systems currently
skipPrimitives(int nPoints,int nPrims,int nPrimAttrib,istream * input,std::ostream * errorStream)160 bool skipPrimitives(int nPoints, int nPrims, int nPrimAttrib, istream* input,std::ostream* errorStream)
161 {
162     int particleSize=0;
163     vector<int> primAttrOffsets; // offsets in # of 32 bit offsets
164     vector<DummyAttribute> primAttrHandles;
165     vector<DummyAccessor> primAccessors;
166     getAttributes(particleSize, primAttrOffsets, primAttrHandles, primAccessors, nPrimAttrib, input, 0, true, errorStream);
167 
168     for(int i=0;i<nPrims;i++) {
169         int primType;
170         read<BIGEND>(*input,primType);
171         if(primType==0x00008000) {
172             int size;
173             read<BIGEND>(*input,size);
174             if(nPoints>=(int)1<<16)
175                 skip(input,size*sizeof(int));
176             else
177                 skip(input,size*sizeof(unsigned short));
178             skip(input,particleSize*sizeof(int));
179         } else {
180             if(errorStream) *errorStream << "Partio: Unrecognized Primitive Type: 0x" << std::hex << primType << " - Cannot process detail attributes" << std::endl;
181             return false;
182         }
183     }
184     return true;
185 }
186 
readBGEO(const char * filename,const bool headersOnly,std::ostream * errorStream)187 ParticlesDataMutable* readBGEO(const char* filename,const bool headersOnly,std::ostream* errorStream)
188 {
189     unique_ptr<istream> input(io::unzip(filename));
190     if(!*input){
191         if(errorStream) *errorStream<<"Partio: Unable to open file "<<filename<<endl;
192         return 0;
193     }
194 
195     // header values
196     char magic[5];
197     magic[4]=0;
198     char versionChar;
199     int version;
200     int nPoints;
201     int nPrims;
202     int nPointGroups;
203     int nPrimGroups;
204     int nPointAttrib;
205     int nVertexAttrib;
206     int nPrimAttrib;
207     int nAttrib;
208     read<BIGEND>(*input,magic[0],magic[1],magic[2],magic[3]);
209     read<BIGEND>(*input,versionChar,version,nPoints,nPrims,nPointGroups);
210     read<BIGEND>(*input,nPrimGroups,nPointAttrib,nVertexAttrib,nPrimAttrib,nAttrib);
211 
212 
213     // Check header magic and version
214     const char bgeo_magic[5]={'B','g','e','o',0};
215     if(strcmp(magic,bgeo_magic)){
216         const char new_bgeo_magic[5]={0x7f,0x4e,0x53,0x4a,0};
217         if(!strcmp(magic,new_bgeo_magic)){
218             if(errorStream) *errorStream<<"Partio: Attempting to read new BGEO format, we only support old BGEO format. Try writing .bhclassic from Houdini."<<std::endl;
219         }else{
220             if(errorStream) *errorStream<<"Partio: Magic number '"<<magic<<" of '"<<filename<<"' doesn't match bgeo magic '"<<bgeo_magic<<endl;
221         }
222         return 0;
223     }
224     if(version!=5){
225         if(errorStream) *errorStream<<"Partio: BGEO must be version 5"<<endl;
226         return 0;
227     }
228 
229     // Allocate a simple particle with the appropriate number of points
230     ParticlesDataMutable* simple=0;
231     if(headersOnly) simple=new ParticleHeaders;
232     else simple=create();
233 
234     simple->addParticles(nPoints);
235 
236 
237     // Read attribute definitions
238     int particleSize=4; // Size in # of 32 bit primitives
239     vector<int> attrOffsets; // offsets in # of 32 bit offsets
240     vector<ParticleAttribute> attrHandles;
241     vector<ParticleAccessor> accessors;
242     attrOffsets.push_back(0); // pull values from byte offset
243     attrHandles.push_back(simple->addAttribute("position",VECTOR,3)); // we always have one
244     accessors.push_back(ParticleAccessor(attrHandles[0]));
245     getAttributes(particleSize, attrOffsets, attrHandles, accessors, nPointAttrib, input.get(), simple, headersOnly, errorStream);
246 
247     if(headersOnly) {
248         skip(input.get(),nPoints*particleSize*sizeof(int));
249     } else {
250         // Read the points
251         int *buffer=new int[particleSize];
252 
253         // make iterator and register accessors
254         ParticlesDataMutable::iterator iterator=simple->begin();
255         for(size_t i=0;i<accessors.size();i++) iterator.addAccessor(accessors[i]);
256 
257         for(ParticlesDataMutable::iterator end=simple->end();iterator!=end;++iterator){
258             input->read((char*)buffer,particleSize*sizeof(int));
259             for(unsigned int attrIndex=0;attrIndex<attrHandles.size();attrIndex++){
260                 ParticleAttribute& handle=attrHandles[attrIndex];
261                 ParticleAccessor& accessor=accessors[attrIndex];
262                 // TODO: this violates strict aliasing, we could just go to char* and make
263                 // a different endian swapper
264                 int* data=accessor.raw<int>(iterator);
265                 for(int k=0;k<handle.count;k++){
266                     BIGEND::swap(buffer[attrOffsets[attrIndex]+k]);
267                     data[k]=buffer[attrOffsets[attrIndex]+k];
268                 }
269             }
270         }
271         delete [] buffer;
272     }
273 
274     if (!skipPrimitives(nPoints, nPrims, nPrimAttrib, input.get(),errorStream)) return simple;
275 
276     particleSize=0;
277     vector<int> fixedAttrOffsets; // offsets in # of 32 bit offsets
278     vector<FixedAttribute> fixedAttrHandles;
279     vector<DummyAccessor> fixedAccessors;
280     getAttributes(particleSize, fixedAttrOffsets, fixedAttrHandles, fixedAccessors, nAttrib, input.get(), simple, headersOnly, errorStream);
281 
282     if (headersOnly) return simple;
283 
284     // Read the points
285     int *fixedBuffer=new int[particleSize];
286     input->read((char*)fixedBuffer,particleSize*sizeof(int));
287     for(unsigned int attrIndex=0;attrIndex<fixedAttrHandles.size();attrIndex++){
288         FixedAttribute& handle=fixedAttrHandles[attrIndex];
289         // TODO: this violates strict aliasing, we could just go to char* and make
290         // a different endian swapper
291         for(int k=0;k<handle.count;k++){
292             BIGEND::swap(fixedBuffer[fixedAttrOffsets[attrIndex]+k]);
293             simple->fixedDataWrite<int>(fixedAttrHandles[attrIndex])[k]=fixedBuffer[fixedAttrOffsets[attrIndex]+k];
294         }
295     }
296     delete [] fixedBuffer;
297 
298     // return the populated simpleParticle
299     return simple;
300 }
301 
writeBGEO(const char * filename,const ParticlesData & p,const bool compressed,std::ostream * errorStream)302 bool writeBGEO(const char* filename,const ParticlesData& p,const bool compressed,std::ostream* errorStream)
303 {
304     unique_ptr<ostream> output(io::write(filename, compressed));
305     if(!*output){
306         if(errorStream) *errorStream <<"Partio Unable to open file "<<filename<<endl;
307         return false;
308     }
309 
310     int magic=((((('B'<<8)|'g')<<8)|'e')<<8)|'o';
311     char versionChar='V';
312     int version=5;
313     int nPoints=p.numParticles();
314     int nPrims=0;
315     int nPointGroups=0;
316     int nPrimGroups=0;
317     int nPointAttrib=p.numAttributes()-1;
318     int nVertexAttrib=0;
319     int nPrimAttrib=0;
320     int nAttrib=p.numFixedAttributes();
321 
322     write<BIGEND>(*output,magic,versionChar,version,nPoints,nPrims,nPointGroups);
323     write<BIGEND>(*output,nPrimGroups,nPointAttrib,nVertexAttrib,nPrimAttrib,nAttrib);
324 
325     vector<ParticleAttribute> handles;
326     vector<ParticleAccessor> accessors;
327     vector<int> attrOffsets;
328     bool foundPosition=false;
329     int particleSize=4;
330     for(int i=0;i<p.numAttributes();i++){
331         ParticleAttribute attr;
332         p.attributeInfo(i,attr);
333         if(attr.name=="position"){
334             attrOffsets.push_back(0);
335             foundPosition=true;
336         }else{
337             writeHoudiniStr(*output,attr.name);
338             if(attr.type==INDEXEDSTR){
339                 int houdiniType=4;
340                 unsigned short size=static_cast<unsigned short>(attr.count);
341                 const std::vector<std::string>& indexTable=p.indexedStrs(attr);
342                 int numIndexes=static_cast<int>(indexTable.size());
343                 write<BIGEND>(*output,size,houdiniType,numIndexes);
344                 for(int i=0;i<numIndexes;i++)
345                     writeHoudiniStr(*output,indexTable[i]);
346             }else{
347                 int houdiniType=0;
348                 switch(attr.type){
349                     case FLOAT: houdiniType=0;break;
350                     case INT: houdiniType=1;break;
351                     case VECTOR: houdiniType=5;break;
352                     case INDEXEDSTR:
353                     case NONE: assert(false);houdiniType=0;break;
354                 }
355                 unsigned short size=static_cast<unsigned short>(attr.count);
356                 write<BIGEND>(*output,size,houdiniType);
357                 for(int i=0;i<attr.count;i++){
358                     int defaultValue=0;
359                     write<BIGEND>(*output,defaultValue);
360                 }
361             }
362             attrOffsets.push_back(particleSize);
363             particleSize+=attr.count;
364         }
365         handles.push_back(attr);
366         accessors.push_back(ParticleAccessor(handles.back()));
367     }
368     if(!foundPosition){
369         if(errorStream) *errorStream <<"Partio: didn't find attr 'position' while trying to write GEO"<<endl;
370         return false;
371     }
372 
373     ParticlesData::const_iterator iterator=p.begin();
374     for(size_t i=0;i<accessors.size();i++) iterator.addAccessor(accessors[i]);
375 
376     int *buffer=new int[particleSize];
377     for(ParticlesData::const_iterator end=p.end();iterator!=end;++iterator){
378         for(unsigned int attrIndex=0;attrIndex<handles.size();attrIndex++){
379             ParticleAttribute& handle=handles[attrIndex];
380             ParticleAccessor& accessor=accessors[attrIndex];
381             // TODO: this violates strict aliasing, we could just go to char* and make
382             // a different endian swapper
383             const int* data=accessor.raw<int>(iterator);
384             for(int k=0;k<handle.count;k++){
385                 buffer[attrOffsets[attrIndex]+k]=data[k];
386                 BIGEND::swap(buffer[attrOffsets[attrIndex]+k]);
387             }
388         }
389         // set homogeneous coordinate
390         float *w=(float*)&buffer[3];
391         *w=1.;
392         BIGEND::swap(*w);
393         output->write((char*)buffer,particleSize*sizeof(int));
394     }
395     delete [] buffer;
396 
397     vector<FixedAttribute> fixedHandles;
398     vector<int> fixedAttrOffsets;
399     particleSize=0;
400     for(int i=0;i<p.numFixedAttributes();i++){
401         FixedAttribute attr;
402         p.fixedAttributeInfo(i,attr);
403 
404         writeHoudiniStr(*output,attr.name);
405         if(attr.type==INDEXEDSTR){
406             int houdiniType=4;
407             unsigned short size=attr.count;
408             const std::vector<std::string>& indexTable=p.fixedIndexedStrs(attr);
409             int numIndexes=indexTable.size();
410             write<BIGEND>(*output,size,houdiniType,numIndexes);
411             for(int ii=0;ii<numIndexes;ii++) {
412                 writeHoudiniStr(*output,indexTable[ii]);
413             }
414         }else{
415             int houdiniType=0;
416             switch(attr.type){
417             case FLOAT: houdiniType=0;break;
418             case INT: houdiniType=1;break;
419             case VECTOR: houdiniType=5;break;
420             case INDEXEDSTR:
421             case NONE: assert(false);houdiniType=0;break;
422             }
423             unsigned short size=attr.count;
424             write<BIGEND>(*output,size,houdiniType);
425             for(int i=0;i<attr.count;i++){
426                 int defaultValue=0;
427                 write<BIGEND>(*output,defaultValue);
428             }
429         }
430         fixedAttrOffsets.push_back(particleSize);
431         particleSize+=attr.count;
432 
433         fixedHandles.push_back(attr);
434     }
435 
436     int *fixedBuffer=new int[particleSize];
437 
438     for(unsigned int attrIndex=0;attrIndex<fixedHandles.size();attrIndex++){
439         FixedAttribute& handle=fixedHandles[attrIndex];
440         // TODO: this violates strict aliasing, we could just go to char* and make
441         // a different endian swapper
442 
443         for(int k=0;k<handle.count;k++){
444 
445             fixedBuffer[fixedAttrOffsets[attrIndex]+k]=p.fixedData<int>(fixedHandles[attrIndex])[k];
446             BIGEND::swap(fixedBuffer[fixedAttrOffsets[attrIndex]+k]);
447         }
448     }
449     output->write((char*)fixedBuffer,particleSize*sizeof(int));
450 
451     delete [] fixedBuffer;
452 
453     // Write extra
454     write<BIGEND>(*output,(char)0x00);
455 #ifdef _MSC_VER
456 	#pragma warning (push)
457 	#pragma warning (disable : 4310 )  // suppress the warning for 0xff being truncated to 0x7f (max value of a signed char)
458 #endif
459     write<BIGEND>(*output,(char)0xff);
460 #ifdef _MSC_VER
461 	#pragma warning (pop)
462 #endif
463 
464     // success
465     return true;
466 }
467 
468 }
469