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