void tSimpleCopyPoints(aiPoints *iobj, aePoints *eobj) { aiPointsData idata; aePointsData edata; int n = aiSchemaGetNumSamples(iobj); for (int i = 0; i < n; ++i) { auto ss = aiIndexToSampleSelector(i); aiSchemaUpdateSample(iobj, &ss); auto *sample = aiSchemaGetSample(iobj, &ss); aiPointsGetDataPointer(sample, &idata); edata.positions = idata.positions; edata.velocities = idata.velocities; edata.ids = idata.ids; edata.count = idata.count; aePointsWriteSample(eobj, &edata); } }
tCLinkage tExport void tPointsExpanderConvert(tContext *tctx_, const PointsExpanderConfig *conf) { tContext& tctx = *tctx_; tLogSetCallback(conf->logCB); tctx.setPointsProcessor([&](aiPoints *iobj, aePoints *eobj) { double time_proc_begin = tGetTime(); const char* target_name = aiGetNameS(aiGetParent(aiSchemaGetObject(iobj))); tLog("processing \"%s\"\n", target_name); tPointsBuffer buf; std::vector<uint64_t> ids, ids_tmp1, ids_tmp2; std::vector<char> point_info; aiPointsSummary summary; aiPointsGetSummary(iobj, &summary); int num_samples = aiSchemaGetNumSamples(iobj); // build list of all ids for (int i = 0; i < num_samples; ++i) { auto ss = aiIndexToSampleSelector(i); aiSchemaUpdateSample(iobj, &ss); auto *sample = aiSchemaGetSample(iobj, &ss); aiPointsData idata; aiPointsGetDataPointer(sample, &idata); ids_tmp1.assign(idata.ids, idata.ids + idata.count); ist::parallel_sort(ids_tmp1.begin(), ids_tmp1.end()); ids_tmp2.resize(ids_tmp1.size() + ids.size()); std::merge(ids_tmp1.begin(), ids_tmp1.end(), ids.begin(), ids.end(), ids_tmp2.begin()); ids_tmp2.erase(std::unique(ids_tmp2.begin(), ids_tmp2.end()), ids_tmp2.end()); ids.assign(ids_tmp2.begin(), ids_tmp2.end()); } tLog(" listed all IDs. %d elements (%.2lfms)\n", (int)ids.size(), tGetTime() - time_proc_begin); // size_t info_size = size_t(sizeof(tPointInfoHeader) + sizeof(tPointInfo) * std::ceil(conf->count_rate)); uint64_t id_range = summary.maxID - summary.minID + 1; point_info.resize(info_size * (size_t)id_range); auto getPointInfoByID = [&](uint64_t id) -> tPointInfoHeader& { return (tPointInfoHeader&)point_info[info_size * size_t(id - summary.minID)]; }; tRandSetSeed(conf->random_seed); uint64_t id_size_scaled = uint64_t((double)ids.size() * conf->count_rate); for (size_t pi = 0; pi < id_size_scaled; ++pi) { size_t spi = size_t((double)pi / conf->count_rate); uint64_t id = ids[spi]; tPointInfo &pf = getPointInfoByID(id).push(); pf.id = (uint32_t)pi; pf.random_diffuse = conf->random_diffuse * tRand3(); } mpKernelParams mpparams; mpparams.damping = conf->repulse_damping; mpparams.advection = conf->repulse_advection; mpparams.max_particles = int(summary.peakCount * std::ceil(conf->count_rate)); mpparams.particle_size = conf->repulse_particle_size; mpparams.pressure_stiffness = conf->repulse_stiffness; auto mp = mpCreateContext(); // process all frames for (int fi = 0; fi < num_samples; ++fi) { double time_frame_begin = tGetTime(); auto ss = aiIndexToSampleSelector(fi); // get points data from alembic aiSchemaUpdateSample(iobj, &ss); auto *sample = aiSchemaGetSample(iobj, &ss); aiPointsData idata; aiPointsGetDataPointer(sample, &idata); // create inc/decreased points buffer size_t num_scaled = 0; for (int pi = 0; pi < idata.count; ++pi) { tPointInfoHeader& pinfo = getPointInfoByID(idata.ids[pi]); num_scaled += pinfo.num; } buf.allocate(num_scaled, idata.velocities != nullptr); for (int pi = 0, spi = 0; pi < idata.count; ++pi) { getPointInfoByID(idata.ids[pi]).each([&](const tPointInfo &pinfo) { buf.positions[spi] = (float3&)idata.positions[pi] + pinfo.random_diffuse; buf.ids[spi] = pinfo.id; if (idata.velocities) { buf.velocities[spi] = (float3&)idata.velocities[pi]; } ++spi; }); } // repulsion if (conf->repulse_iteration > 0) { mpparams.world_center = (mpV3&)idata.center; mpparams.world_extent = (mpV3&)idata.size; mpparams.world_div = mpV3i( std::min<int>(int(mpparams.world_extent.x * 2.0f / conf->repulse_particle_size), 256), std::min<int>(int(mpparams.world_extent.y * 2.0f / conf->repulse_particle_size), 256), std::min<int>(int(mpparams.world_extent.z * 2.0f / conf->repulse_particle_size), 256) ); // apply repulsion mpSetKernelParams(mp, &mpparams); mpClearParticles(mp); mpForceSetNumParticles(mp, (int)num_scaled); mpParticle *particles = mpGetParticles(mp); for (size_t pi = 0; pi < num_scaled; ++pi) { particles[pi].position = (mpV3&)buf.positions[pi]; particles[pi].velocity = mpV3(); particles[pi].lifetime = std::numeric_limits<float>::max(); particles[pi].userdata = (int)pi; } for (int ri = 0; ri < conf->repulse_iteration; ++ri) { mpUpdate(mp, conf->repulse_timestep); } ist::parallel_sort(particles, particles + num_scaled, [](const mpParticle& a, const mpParticle& b) { return a.userdata < b.userdata; }); for (size_t pi = 0; pi < num_scaled; ++pi) { buf.positions[pi] = (float3&)particles[pi].position; } if (idata.velocities) { for (size_t pi = 0; pi < num_scaled; ++pi) { buf.velocities[pi] = (float3&)particles[pi].velocity; } } } auto edata = buf.asExportData(); aePointsWriteSample(eobj, &edata); tLog(" frame %d: %d -> %d points (%.2lfms)\n", fi, idata.count, (int)num_scaled, tGetTime() - time_frame_begin); } mpDestroyContext(mp); tLog("finished \"%s\" (%.2lfms)\n", target_name, tGetTime() - time_proc_begin); }); tctx.doExport(); }