// the bit that does all the work OP_ERROR SOP_rmanPtc::cookMySop(OP_Context &context) { // get some useful bits & bobs UT_Interrupt *boss; float now = context.myTime; UT_String ptcFile = getPtcFile(now); int loadPercentage = getLoadPercentage(now); int displayPercentage = getDisplayPercentage(now); float pointSize = getPointSize(now); int useDisk = getUseDisk(now); int boundOnLoad = getBoundOnLoad(now); int displayChannel = getDisplayChannel(now); int onlyOutputDisplayChannel = getOnlyOutputDisplayChannel(now); /* float nearDensity = getNearDensity(now); float farDensity = getFarDensity(now); UT_String cullCamera = getCullCamera(now); */ // lock out inputs if ( lockInputs(context) >= UT_ERROR_ABORT) error(); // Check to see that there hasn't been a critical error in cooking the SOP. if (error() < UT_ERROR_ABORT) { boss = UTgetInterrupt(); // here we make sure our detail is an instance of rmanPtcDetail rmanPtcDetail *ptc_gdp = dynamic_cast<rmanPtcSop::rmanPtcDetail *>(gdp); if ( !ptc_gdp ) ptc_gdp = allocateNewDetail(); // clear our gdp ptc_gdp->clearAndDestroy(); // start our work boss->opStart("Loading point cloud"); // get our bbox bool has_bbox = false; const GU_Detail *input_geo = inputGeo( 0, context ); updateBBox( input_geo ); // pass information to our detail ptc_gdp->point_size = pointSize; ptc_gdp->use_disk = useDisk; ptc_gdp->cull_bbox = mBBox; ptc_gdp->use_cull_bbox = (mHasBBox&&(!mBoundOnLoad))?true:false; ptc_gdp->display_probability = displayPercentage/100.f; ptc_gdp->display_channel = displayChannel; if ( onlyOutputDisplayChannel ) ptc_gdp->display_channel = 0; // here we load our ptc if ( mReload ) { // clear everything mChannelNames.clear(); cachePoints.clear(); cacheNormals.clear(); cacheRadius.clear(); cacheData.clear(); mRedraw = true; // open the point cloud PtcPointCloud ptc = PtcSafeOpenPointCloudFile( const_cast<char*>(ptcFile.buffer()) ); if ( !ptc ) { UT_String msg( "Unable to open input file: " ); msg += ptcFile; addError( SOP_MESSAGE, msg); boss->opEnd(); return error(); } // get some information from the ptc ptc_gdp->path = std::string(ptcFile.fileName()); char **vartypes, **varnames; PtcGetPointCloudInfo( ptc, const_cast<char*>("npoints"), &ptc_gdp->nPoints ); PtcGetPointCloudInfo( ptc, const_cast<char*>("npointvars"), &ptc_gdp->nChannels ); PtcGetPointCloudInfo( ptc, const_cast<char*>("pointvartypes"), &vartypes ); PtcGetPointCloudInfo( ptc, const_cast<char*>("pointvarnames"), &varnames ); PtcGetPointCloudInfo( ptc, const_cast<char*>("datasize"), &ptc_gdp->datasize ); PtcGetPointCloudInfo( ptc, const_cast<char*>("bbox"), ptc_gdp->bbox ); // process our channel names ptc_gdp->types.clear(); ptc_gdp->names.clear(); for ( unsigned int i=0; i<ptc_gdp->nChannels; ++i ) { ptc_gdp->types.push_back( std::string(vartypes[i]) ); ptc_gdp->names.push_back( std::string(varnames[i]) ); std::string name = ptc_gdp->types[i] + " " + ptc_gdp->names[i]; mChannelNames.push_back( name ); } // what percentage of points should we load? float load_probability = loadPercentage/100.f; // load points into memory float point[3], normal[3]; float radius; float data[ptc_gdp->datasize]; srand(0); for ( unsigned int i=0; i<ptc_gdp->nPoints; ++i ) { PtcReadDataPoint( ptc, point, normal, &radius, data ); // bound on load if ( boundOnLoad ) { UT_Vector3 pt( point[0], point[1], point[2] ); if ( !mBBox.isInside(pt) ) continue; } // discard a percentage of our points if ( rand()/(float)RAND_MAX>load_probability ) continue; // put points into our cache cachePoints.push_back( point ); cacheNormals.push_back( normal ); cacheRadius.push_back( radius ); for ( unsigned int j=0; j<ptc_gdp->datasize; ++j ) cacheData.push_back( data[j] ); // break for the interrupt handler (i.e. press ESC) if ( boss->opInterrupt() ) break; } ptc_gdp->nLoaded = cachePoints.size(); // mark our detail as valid and close our ptc PtcClosePointCloudFile( ptc ); mReload = false; // force update on channel parameter getParm("chan").revertToDefaults(now); } // build a new primitive GU_PrimParticle::build( ptc_gdp, cachePoints.size(), 0 ); // create our output geometry using the output % parameter // this is the same variable as GR_ uses to preview std::vector<UT_Vector3>::const_iterator pos_it = cachePoints.begin(); std::vector<UT_Vector3>::const_iterator norm_it = cacheNormals.begin(); std::vector<float>::const_iterator rad_it = cacheRadius.begin(); std::vector<float>::const_iterator data_it = cacheData.begin(); // add some standard attributes GB_AttributeRef n_attrib = ptc_gdp->addPointAttrib( "N", sizeof(UT_Vector3), GB_ATTRIB_VECTOR, 0 ); GB_AttributeRef r_attrib = ptc_gdp->addPointAttrib( "radius", sizeof(float), GB_ATTRIB_FLOAT, 0 ); ptc_gdp->N_attrib = n_attrib; ptc_gdp->R_attrib = r_attrib; // process the rest of our data attributes std::vector<GB_AttribType> data_types; std::vector<GB_AttributeRef> data_attribs; std::vector<int> data_size, data_offset; int offset_total = 0; ptc_gdp->attributes.clear(); ptc_gdp->attribute_size.clear(); for ( unsigned int i=0; i<ptc_gdp->nChannels; ++i ) { GB_AttribType type = GB_ATTRIB_FLOAT; // float, vector int size = 1; if ( ptc_gdp->types[i] == "point" || ptc_gdp->types[i] == "vector" || ptc_gdp->types[i] == "normal" ) { type = GB_ATTRIB_VECTOR; size = 3; } if ( ptc_gdp->types[i] == "color" ) { size=3; } if ( ptc_gdp->types[i] == "matrix" ) { size=16; } offset_total += size; data_types.push_back( type ); data_size.push_back( size ); data_offset.push_back( offset_total ); if ( onlyOutputDisplayChannel ) { if ( displayChannel==i ) { GB_AttributeRef attrib = ptc_gdp->addPointAttrib( ptc_gdp->names[i].c_str(), sizeof(float)*size, type, 0 ); ptc_gdp->attributes.push_back( attrib ); ptc_gdp->attribute_size.push_back( size ); data_attribs.push_back( attrib ); } } else { GB_AttributeRef attrib = ptc_gdp->addPointAttrib( ptc_gdp->names[i].c_str(), sizeof(float)*size, type, 0 ); ptc_gdp->attributes.push_back( attrib ); ptc_gdp->attribute_size.push_back( size ); data_attribs.push_back( attrib ); } } cacheDataOffsets = data_offset; /* // cull camera bool use_cull_cam = false; UT_Vector3 cam_pos(0,0,0); float cam_near=0.0, cam_far=0.0; if ( cullCamera!="" ) { OBJ_Node *cam_node = OPgetDirector()->findOBJNode( cullCamera ); if ( cam_node ) { OBJ_Camera *cam = cam_node->castToOBJCamera(); if ( cam ) { UT_DMatrix4 mtx; cam->getWorldTransform( mtx, context ); std::cerr << "mtx: " << mtx << std::endl; cam_pos *= mtx; std::cerr << "pos: " << cam_pos << std::endl; use_cull_cam = true; cam_near = cam->getNEAR(now); cam_far = cam->getFAR(now); std::cerr << "near: " << cam_near << std::endl; std::cerr << "far: " << cam_far << std::endl; mRedraw = true; } } std::cerr << "cull camera: " << cullCamera << std::endl; std::cerr << nearDensity << ", " << farDensity << std::endl; } */ // add data from our cached points to geometry // based on display/output probability srand(0); float density_mult = 1.f; while( pos_it!=cachePoints.end() ) { /* if ( use_cull_cam ) { float dist = ((*pos_it)-cam_pos).length(); if ( dist<cam_near ) density_mult = nearDensity; else if ( dist>cam_far ) density_mult = farDensity; else { float normalize_dist =( dist - cam_near ) / ( cam_far - cam_near ); density_mult = 1.f - normalize_dist; } } */ if ( rand()/(float)RAND_MAX < ptc_gdp->display_probability*density_mult ) { if ( (!ptc_gdp->use_cull_bbox) || (ptc_gdp->use_cull_bbox && ptc_gdp->cull_bbox.isInside( *pos_it ) ) ) { // add to our SOP geometry GEO_Point *pt = ptc_gdp->appendPoint(); pt->setPos( *pos_it ); (*pt->castAttribData<UT_Vector3>(n_attrib)) = *norm_it; (*pt->castAttribData<float>(r_attrib)) = *rad_it; const float *data = &*data_it; for ( unsigned int i=0; i<data_types.size(); ++i ) { if ( onlyOutputDisplayChannel ) { if ( i==displayChannel ) { pt->set( data_attribs[0], data, data_size[i] ); } data += data_size[i]; } else { pt->set( data_attribs[i], data, data_size[i] ); data += data_size[i]; } } } } // increment our interators pos_it++; norm_it++; rad_it++; data_it+=ptc_gdp->datasize; } // delete our particle primitive ptc_gdp->deletePrimitive(0,0); // info in sop's message area std::stringstream ss; ss << "Name: " << ptc_gdp->path << std::endl; ss << "Points: " << ptc_gdp->nPoints << " - [ " << ptc_gdp->nLoaded << " loaded ]" << std::endl; ss << "Channels: " << ptc_gdp->nChannels << std::endl; for ( unsigned int i=0; i<ptc_gdp->nChannels; ++i ) ss << " " << i << ": " << mChannelNames[i] << std::endl; addMessage( SOP_MESSAGE, ss.str().c_str() ); // Tell the interrupt server that we've completed boss->opEnd(); // force update? ptc_gdp->redraw = mRedraw; mRedraw = false; } // tidy up & go home unlockInputs(); return error(); }