tahoma2d/toonz/sources/stdfx/iwa_particlesengine.cpp
2016-06-20 11:34:17 +09:00

1589 lines
62 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//------------------------------------------------------------------
// Iwa_Particles_Engine for Marnie
// based on Particles_Engine by Digital Video
//------------------------------------------------------------------
#include "trop.h"
#include "tfxparam.h"
#include "tofflinegl.h"
#include "tstopwatch.h"
#include "tsystem.h"
#include "timagecache.h"
#include "tconvert.h"
#include "tflash.h"
#include "trasterimage.h"
#include "timage_io.h"
#include "tcolorfunctions.h"
#include "toonz/tcolumnfx.h"
#include "iwa_particlesmanager.h"
#include "iwa_particlesengine.h"
#include "trenderer.h"
#include <QMutex>
#include <QMutexLocker>
#include <strstream>
namespace {
QMutex mutex;
void printTime(TStopWatch &sw, std::string name) {
std::ostrstream ss;
ss << name << " : ";
sw.print(ss);
ss << '\n' << '\0';
TSystem::outputDebug(ss.str());
}
};
//----
/*-----------------------------------------------------------------*/
Iwa_Particles_Engine::Iwa_Particles_Engine(Iwa_TiledParticlesFx *parent,
double frame)
: m_parent(parent), m_frame(frame) {}
/*-----------------------------------------------------------------*/
void Iwa_Particles_Engine::fill_value_struct(struct particles_values &myvalues,
double frame) {
myvalues.source_ctrl_val = m_parent->source_ctrl_val->getValue();
myvalues.bright_thres_val = m_parent->bright_thres_val->getValue();
myvalues.x_pos_val = m_parent->center_val->getValue(frame).x;
myvalues.y_pos_val = m_parent->center_val->getValue(frame).y;
myvalues.length_val = m_parent->length_val->getValue(frame);
myvalues.height_val = m_parent->height_val->getValue(frame);
myvalues.maxnum_val = m_parent->maxnum_val->getValue(frame);
myvalues.lifetime_val = m_parent->lifetime_val->getValue(frame);
myvalues.lifetime_ctrl_val = m_parent->lifetime_ctrl_val->getValue();
myvalues.column_lifetime_val = m_parent->column_lifetime_val->getValue();
myvalues.startpos_val = m_parent->startpos_val->getValue();
myvalues.randseed_val = m_parent->randseed_val->getValue();
myvalues.gravity_val = m_parent->gravity_val->getValue(frame);
myvalues.g_angle_val = m_parent->g_angle_val->getValue(frame);
myvalues.gravity_ctrl_val = m_parent->gravity_ctrl_val->getValue();
myvalues.friction_val = m_parent->friction_val->getValue(frame);
myvalues.friction_ctrl_val = m_parent->friction_ctrl_val->getValue();
myvalues.windint_val = m_parent->windint_val->getValue(frame);
myvalues.windangle_val = m_parent->windangle_val->getValue(frame);
myvalues.swingmode_val = m_parent->swingmode_val->getValue();
myvalues.randomx_val = m_parent->randomx_val->getValue(frame);
myvalues.randomy_val = m_parent->randomy_val->getValue(frame);
myvalues.randomx_ctrl_val = m_parent->randomx_ctrl_val->getValue();
myvalues.randomy_ctrl_val = m_parent->randomy_ctrl_val->getValue();
myvalues.swing_val = m_parent->swing_val->getValue(frame);
myvalues.speed_val = m_parent->speed_val->getValue(frame);
myvalues.speed_ctrl_val = m_parent->speed_ctrl_val->getValue();
myvalues.speeda_val = m_parent->speeda_val->getValue(frame);
myvalues.speeda_ctrl_val = m_parent->speeda_ctrl_val->getValue();
myvalues.speeda_use_gradient_val =
m_parent->speeda_use_gradient_val->getValue();
myvalues.speedscale_val = m_parent->speedscale_val->getValue();
myvalues.toplayer_val = m_parent->toplayer_val->getValue();
myvalues.mass_val = m_parent->mass_val->getValue(frame);
myvalues.scale_val = m_parent->scale_val->getValue(frame);
myvalues.scale_ctrl_val = m_parent->scale_ctrl_val->getValue();
myvalues.scale_ctrl_all_val = m_parent->scale_ctrl_all_val->getValue();
myvalues.rot_val = m_parent->rot_val->getValue(frame);
myvalues.rot_ctrl_val = m_parent->rot_ctrl_val->getValue();
myvalues.trail_val = m_parent->trail_val->getValue(frame);
myvalues.trailstep_val = m_parent->trailstep_val->getValue(frame);
myvalues.rotswingmode_val = m_parent->rotswingmode_val->getValue();
myvalues.rotspeed_val = m_parent->rotspeed_val->getValue(frame);
myvalues.rotsca_val = m_parent->rotsca_val->getValue(frame);
myvalues.rotswing_val = m_parent->rotswing_val->getValue(frame);
myvalues.pathaim_val = m_parent->pathaim_val->getValue();
myvalues.opacity_val = m_parent->opacity_val->getValue(frame);
myvalues.opacity_ctrl_val = m_parent->opacity_ctrl_val->getValue();
myvalues.trailopacity_val = m_parent->trailopacity_val->getValue(frame);
myvalues.scalestep_val = m_parent->scalestep_val->getValue(frame);
myvalues.scalestep_ctrl_val = m_parent->scalestep_ctrl_val->getValue();
myvalues.fadein_val = m_parent->fadein_val->getValue(frame);
myvalues.fadeout_val = m_parent->fadeout_val->getValue(frame);
myvalues.animation_val = m_parent->animation_val->getValue();
myvalues.step_val = m_parent->step_val->getValue();
myvalues.gencol_val = m_parent->gencol_val->getValue(frame);
myvalues.gencol_ctrl_val = m_parent->gencol_ctrl_val->getValue();
myvalues.gencol_spread_val = m_parent->gencol_spread_val->getValue(frame);
myvalues.genfadecol_val = m_parent->genfadecol_val->getValue(frame);
myvalues.fincol_val = m_parent->fincol_val->getValue(frame);
myvalues.fincol_ctrl_val = m_parent->fincol_ctrl_val->getValue();
myvalues.fincol_spread_val = m_parent->fincol_spread_val->getValue(frame);
myvalues.finrangecol_val = m_parent->finrangecol_val->getValue(frame);
myvalues.finfadecol_val = m_parent->finfadecol_val->getValue(frame);
myvalues.foutcol_val = m_parent->foutcol_val->getValue(frame);
myvalues.foutcol_ctrl_val = m_parent->foutcol_ctrl_val->getValue();
myvalues.foutcol_spread_val = m_parent->foutcol_spread_val->getValue(frame);
myvalues.foutrangecol_val = m_parent->foutrangecol_val->getValue(frame);
myvalues.foutfadecol_val = m_parent->foutfadecol_val->getValue(frame);
myvalues.source_gradation_val = m_parent->source_gradation_val->getValue();
myvalues.pick_color_for_every_frame_val =
m_parent->pick_color_for_every_frame_val->getValue();
/*- 計算モード (背景+粒子/粒子/背景/照明された粒子) -*/
myvalues.iw_rendermode_val = m_parent->iw_rendermode_val->getValue();
/*- 粒子に貼られる絵の素材 -*/
myvalues.base_ctrl_val = m_parent->base_ctrl_val->getValue();
/*- カールノイズ的な動きを与える -*/
myvalues.curl_val = m_parent->curl_val->getValue(frame);
myvalues.curl_ctrl_1_val = m_parent->curl_ctrl_1_val->getValue();
myvalues.curl_ctrl_2_val = m_parent->curl_ctrl_2_val->getValue();
/*- 粒子敷き詰め。粒子を正三角形で敷き詰めたときの、
正三角形の一辺の長さをインチで指定する -*/
myvalues.iw_triangleSize = m_parent->iw_triangleSize->getValue(frame);
/*- ひらひら回転 -*/
myvalues.flap_ctrl_val = m_parent->flap_ctrl_val->getValue();
myvalues.iw_flap_velocity_val =
m_parent->iw_flap_velocity_val->getValue(frame);
myvalues.iw_flap_dir_sensitivity_val =
m_parent->iw_flap_dir_sensitivity_val->getValue(frame);
/*- ひらひら粒子に照明を当てる normalize_values()内で Degree → Radian 化する
* -*/
myvalues.iw_light_theta_val = m_parent->iw_light_theta_val->getValue(frame);
myvalues.iw_light_phi_val = m_parent->iw_light_phi_val->getValue(frame);
/*- 読み込みマージン -*/
myvalues.margin_val = m_parent->margin_val->getValue(frame);
/*- 重力を徐々に与えるためのフレーム長 -*/
myvalues.iw_gravityBufferFrame_val =
m_parent->iw_gravityBufferFrame_val->getValue();
}
/*-----------------------------------------------------------------*/
void Iwa_Particles_Engine::fill_range_struct(struct particles_values &values,
struct particles_ranges &ranges) {
ranges.swing_range = values.swing_val.second - values.swing_val.first;
ranges.rotswing_range =
values.rotswing_val.second - values.rotswing_val.first;
ranges.randomx_range = values.randomx_val.second - values.randomx_val.first;
ranges.randomy_range = values.randomy_val.second - values.randomy_val.first;
ranges.rotsca_range = values.rotsca_val.second - values.rotsca_val.first;
ranges.rot_range = values.rot_val.second - values.rot_val.first;
ranges.speed_range = values.speed_val.second - values.speed_val.first;
ranges.speeda_range = values.speeda_val.second - values.speeda_val.first;
ranges.mass_range = values.mass_val.second - values.mass_val.first;
ranges.scale_range = values.scale_val.second - values.scale_val.first;
ranges.lifetime_range =
values.lifetime_val.second - values.lifetime_val.first;
ranges.scalestep_range =
values.scalestep_val.second - values.scalestep_val.first;
ranges.trail_range = (int)(values.trail_val.second - values.trail_val.first);
}
bool Iwa_Particles_Engine::port_is_used(int i,
struct particles_values &values) {
return values.fincol_ctrl_val == i || values.foutcol_ctrl_val == i ||
values.friction_ctrl_val == i || values.gencol_ctrl_val == i ||
values.gravity_ctrl_val == i || values.opacity_ctrl_val == i ||
values.rot_ctrl_val == i || values.scale_ctrl_val == i ||
values.scalestep_ctrl_val == i || values.source_ctrl_val == i ||
values.speed_ctrl_val == i || values.speeda_ctrl_val == i ||
values.lifetime_ctrl_val == i || values.randomx_ctrl_val == i ||
values.randomy_ctrl_val == i || values.base_ctrl_val == i ||
values.curl_ctrl_1_val == i || values.curl_ctrl_2_val == i ||
values.flap_ctrl_val == i;
}
/*-----------------------------------------------------------------*/
//-------------
/*- Startフレームからカレントフレームまで順番に回す関数 生成もここで -*/
void Iwa_Particles_Engine::roll_particles(
TTile *tile, /*-結果を格納するTile-*/
std::map<int, TTile *> porttiles, /*-コントロール画像のポート番号/タイル-*/
const TRenderSettings &ri, /*-現在のフレームの計算用RenderSettings-*/
std::list<Iwa_Particle> &myParticles, /*-パーティクルのリスト-*/
struct particles_values &values, /*-現在のフレームでのパラメータ-*/
float cx, /*- 0 で入ってくる-*/
float cy, /*- 0 で入ってくる-*/
int frame, /*-現在のフレーム値forで回す値-*/
int curr_frame, /*-カレントフレーム-*/
int level_n, /*-テクスチャ素材画像の数-*/
bool *random_level, /*-ループの最初にfalseで入ってくる-*/
float dpi, /*- 1 で入ってくる-*/
std::vector<int> lastframe, /*-テクスチャ素材のそれぞれのカラム長-*/
int &totalparticles, QList<ParticleOrigin> &particleOrigins,
int genPartNum /*- 実際に生成したい粒子数 -*/
) {
particles_ranges ranges;
int i;
float xgravity, ygravity, windx, windy;
/*- 風の強さ重力の強さをX,Y成分に分ける -*/
windx = values.windint_val * sin(values.windangle_val);
windy = values.windint_val * cos(values.windangle_val);
xgravity = values.gravity_val * sin(values.g_angle_val);
ygravity = values.gravity_val * cos(values.g_angle_val);
fill_range_struct(values, ranges);
std::vector<TPointD> myregions;
QList<QList<int>> myHistogram;
std::map<int, TTile *>::iterator it = porttiles.find(values.source_ctrl_val);
/*- ソース画像にコントロールが付いていた場合 -*/
if (values.source_ctrl_val && (it != porttiles.end()) &&
it->second->getRaster())
/*- 入力画像のアルファ値に比例して発生濃度を変える -*/
fill_single_region(myregions, it->second, values.bright_thres_val,
values.source_gradation_val, myHistogram,
particleOrigins);
/*- 粒子が出きったらもう出さない -*/
int actualBirthParticles = std::min(genPartNum, particleOrigins.size());
/*- 出発する粒子のインデックス -*/
QList<int> leavingPartIndex;
if (myregions.size() &&
values.source_ctrl_val != Iwa_TiledParticlesFx::CTRL_NONE) {
int partLeft = actualBirthParticles;
while (partLeft > 0) {
int PrePartLeft = partLeft;
int potential_sum = 0;
QList<int> myWeight;
/*- 各濃度のポテンシャルの大きさmyWeightと、合計ポテンシャルを計算 -*/
for (int m = 0; m < 256; m++) {
int pot = myHistogram[m].size() * m;
myWeight.append(pot);
potential_sum += pot;
}
/*- 各濃度について(濃い方から) -*/
for (int m = 255; m > 0; m--) {
/*- 割り当てを計算(切り上げ) -*/
int wariate = tceil((float)(myWeight[m]) * (float)(partLeft) /
(float)potential_sum);
/*- 実際にこのポテンシャルから出発する粒子数 -*/
int leaveNum = std::min(myHistogram.at(m).size(), wariate);
/*- 割り当てられた粒子を頭から登録 -*/
for (int lp = 0; lp < leaveNum; lp++) {
/*- Histogramから減らしながら追加 -*/
leavingPartIndex.append(myHistogram[m].takeFirst());
/*- 残数を減らす -*/
partLeft--;
if (partLeft == 0) break;
}
if (partLeft == 0) break;
}
/*- ひとつも出発出来なければbreak -*/
if (partLeft == PrePartLeft) break;
}
/*- 実際に飛び出せた粒子数 -*/
actualBirthParticles = leavingPartIndex.size();
} else /*- 何も刺さっていなければ、ランダムに発生させる -*/
{
for (int i = 0; i < actualBirthParticles; i++) leavingPartIndex.append(i);
}
/*- 動かす粒子も生まれる粒子も無い -*/
if (myParticles.empty() && actualBirthParticles == 0) {
std::cout << "no particles generated nor alive. returning function"
<< std::endl;
return;
}
/*- 背景だけを描画するモードのときは、particlesOriginを更新するだけでOK -*/
if (values.iw_rendermode_val == Iwa_TiledParticlesFx::REND_BG) {
/*- インデックスを小さい順にならべる -*/
qSort(leavingPartIndex.begin(), leavingPartIndex.end());
/*- インデックス大きい方から消していく -*/
for (int lp = leavingPartIndex.size() - 1; lp >= 0; lp--)
particleOrigins.removeAt(leavingPartIndex.at(lp));
return;
}
/*- 新規粒子の生成 -*/
/*- 新規粒子しかない場合 -*/
if (myParticles.empty() && actualBirthParticles) // Initial creation
{
/*- 新たに作るパーティクルの数だけループ -*/
for (i = 0; i < actualBirthParticles; i++) {
/*- 出発する粒子 -*/
ParticleOrigin po = particleOrigins.at(leavingPartIndex.at(i));
/*- どのTextureレベルを使うか -*/
int seed = (int)((std::numeric_limits<int>::max)() *
values.random_val->getFloat());
/*- Lifetimeを得る -*/
int lifetime = 0;
if (values.column_lifetime_val)
lifetime = lastframe[po.level];
else {
lifetime = (int)(values.lifetime_val.first +
ranges.lifetime_range * values.random_val->getFloat());
}
/*-
* この粒子が、レンダリングするフレームでも生きているか判断し、生きているなら生成
* -*/
if (lifetime > curr_frame - frame) {
myParticles.push_back(Iwa_Particle(
lifetime, seed, porttiles, values, ranges, totalparticles, 0,
(int)po.level, lastframe[po.level], po.pos[0],
po.pos[1], /*- 座標を指定して発生 -*/
po.isUpward, /*- orientation を追加 -*/
(int)po.initSourceFrame) /*- 素材内の初期フレーム位置 -*/
);
}
totalparticles++;
}
}
/*- 既存粒子を動かし、かつ新規粒子を作る -*/
else {
std::list<Iwa_Particle>::iterator it;
for (it = myParticles.begin(); it != myParticles.end();) {
std::list<Iwa_Particle>::iterator current = it;
++it;
Iwa_Particle &part = (*current);
if (part.scale <= 0.0) continue;
if (part.lifetime <= 0) // Note: This is in line with the above
// "lifetime>curr_frame-frame"
myParticles.erase(current); // insertion counterpart
else {
part.move(porttiles, values, ranges, windx, windy, xgravity, ygravity,
dpi, lastframe[part.level]);
}
}
switch (values.toplayer_val) {
case Iwa_TiledParticlesFx::TOP_YOUNGER:
for (i = 0; i < actualBirthParticles; i++) {
/*- 出発する粒子 -*/
ParticleOrigin po = particleOrigins.at(leavingPartIndex.at(i));
int seed = (int)((std::numeric_limits<int>::max)() *
values.random_val->getFloat());
int lifetime = 0;
if (values.column_lifetime_val)
lifetime = lastframe[po.level];
else {
lifetime =
(int)(values.lifetime_val.first +
ranges.lifetime_range * values.random_val->getFloat());
}
if (lifetime > curr_frame - frame) {
myParticles.push_front(Iwa_Particle(
lifetime, seed, porttiles, values, ranges, totalparticles, 0,
(int)po.level, lastframe[po.level], po.pos[0], po.pos[1],
po.isUpward,
(int)po.initSourceFrame) /*- 素材内の初期フレーム位置 -*/
);
}
totalparticles++;
}
break;
case Iwa_TiledParticlesFx::TOP_RANDOM:
for (i = 0; i < actualBirthParticles; i++) {
double tmp = values.random_val->getFloat() * myParticles.size();
std::list<Iwa_Particle>::iterator it = myParticles.begin();
for (int j = 0; j < tmp; j++, it++)
;
{
/*- 出発する粒子 -*/
ParticleOrigin po = particleOrigins.at(leavingPartIndex.at(i));
int seed = (int)((std::numeric_limits<int>::max)() *
values.random_val->getFloat());
int lifetime = 0;
if (values.column_lifetime_val)
lifetime = lastframe[po.level];
else {
lifetime =
(int)(values.lifetime_val.first +
ranges.lifetime_range * values.random_val->getFloat());
}
if (lifetime > curr_frame - frame) {
myParticles.insert(
it,
Iwa_Particle(
lifetime, seed, porttiles, values, ranges, totalparticles,
0, (int)po.level, lastframe[po.level], po.pos[0], po.pos[1],
po.isUpward,
(int)po.initSourceFrame) /*- 素材内の初期フレーム位置 -*/
);
}
totalparticles++;
}
}
break;
default:
for (i = 0; i < actualBirthParticles; i++) {
/*- 出発する粒子 -*/
ParticleOrigin po = particleOrigins.at(leavingPartIndex.at(i));
int seed = (int)((std::numeric_limits<int>::max)() *
values.random_val->getFloat());
int lifetime = 0;
if (values.column_lifetime_val)
lifetime = lastframe[po.level];
else
lifetime =
(int)(values.lifetime_val.first +
ranges.lifetime_range * values.random_val->getFloat());
if (lifetime > curr_frame - frame) {
myParticles.push_back(Iwa_Particle(
lifetime, seed, porttiles, values, ranges, totalparticles, 0,
(int)po.level, lastframe[po.level], po.pos[0], po.pos[1],
po.isUpward,
(int)po.initSourceFrame) /*- 素材内の初期フレーム位置 -*/
);
}
totalparticles++;
}
break;
}
}
/*- すでに発生したparticleOriginを消去する
インデックスを小さい順にならべる -*/
qSort(leavingPartIndex.begin(), leavingPartIndex.end());
/*- インデックス大きい方から消していく -*/
for (int lp = leavingPartIndex.size() - 1; lp >= 0; lp--)
particleOrigins.removeAt(leavingPartIndex.at(lp));
}
/*-----------------------------------------------------------------*/
void Iwa_Particles_Engine::normalize_values(struct particles_values &values,
const TRenderSettings &ri) {
double dpicorr = 1;
TPointD pos(values.x_pos_val, values.y_pos_val);
(values.x_pos_val) = pos.x;
(values.y_pos_val) = pos.y;
(values.length_val) = (values.length_val) * dpicorr;
(values.height_val) = (values.height_val) * dpicorr;
(values.gravity_val) = (values.gravity_val) * dpicorr * 0.1;
(values.windint_val) = (values.windint_val) * dpicorr;
(values.speed_val.first) = (values.speed_val.first) * dpicorr;
(values.speed_val.second) = (values.speed_val.second) * dpicorr;
(values.randomx_val.first) = (values.randomx_val.first) * dpicorr;
(values.randomx_val.second) = (values.randomx_val.second) * dpicorr;
(values.randomy_val.first) = (values.randomy_val.first) * dpicorr;
(values.randomy_val.second) = (values.randomy_val.second) * dpicorr;
(values.scale_val.first) = (values.scale_val.first) * 0.01;
(values.scale_val.second) = (values.scale_val.second) * 0.01;
(values.scalestep_val.first) = (values.scalestep_val.first) * 0.01;
(values.scalestep_val.second) = (values.scalestep_val.second) * 0.01;
(values.opacity_val.first) = (values.opacity_val.first) * 0.01;
(values.opacity_val.second) = (values.opacity_val.second) * 0.01;
(values.trailopacity_val.first) = (values.trailopacity_val.first) * 0.01;
(values.trailopacity_val.second) = (values.trailopacity_val.second) * 0.01;
(values.mblur_val) = (values.mblur_val) * 0.01;
(values.friction_val) = -(values.friction_val) * 0.01;
(values.windangle_val) = (values.windangle_val) * M_PI_180;
(values.g_angle_val) = (values.g_angle_val + 180) * M_PI_180;
(values.speeda_val.first) = (values.speeda_val.first) * M_PI_180;
(values.speeda_val.second) = (values.speeda_val.second) * M_PI_180;
if (values.step_val < 1) values.step_val = 1;
values.genfadecol_val = (values.genfadecol_val) * 0.01;
values.finfadecol_val = (values.finfadecol_val) * 0.01;
values.foutfadecol_val = (values.foutfadecol_val) * 0.01;
(values.curl_val) = (values.curl_val) * dpicorr * 0.1;
/*- ひらひら粒子に照明を当てる normalize_values()内で Degree → Radian 化する
* -*/
(values.iw_light_theta_val) = (values.iw_light_theta_val) * M_PI_180;
(values.iw_light_phi_val) = (values.iw_light_phi_val) * M_PI_180;
/*- 読み込みマージン -*/
(values.margin_val) = (values.margin_val) * dpicorr;
}
/*-----------------------------------------------------------------*/
void Iwa_Particles_Engine::render_particles(
TFlash *flash, /*- 0 が入ってくる -*/
TTile *tile, /*- 結果を格納するTile -*/
std::vector<TRasterFxPort *> part_ports, /*- テクスチャ素材画像のポート -*/
const TRenderSettings &ri,
TDimension
&p_size, /*- テクスチャ素材のバウンディングボックスの足し合わさったもの
-*/
TPointD &p_offset, /*- バウンディングボックス左下の座標 -*/
std::map<int, TRasterFxPort *>
ctrl_ports, /*- コントロール画像のポート番号/ポート -*/
std::vector<TLevelP>
partLevel, /*- テクスチャ素材のリスト -*/
float dpi, /*- 1 が入ってくる -*/
int curr_frame,
int shrink, /*- 1 が入ってくる -*/
double startx, /*- 0 が入ってくる -*/
double starty, /*- 0 が入ってくる -*/
double endx, /*- 0 が入ってくる -*/
double endy, /*- 0 が入ってくる -*/
std::vector<int> last_frame, /*- テクスチャ素材のそれぞれのカラム長 -*/
unsigned long fxId) {
/*- 各種パーティクルのパラメータ -*/
struct particles_values values;
memset(&values, 0, sizeof(values));
/*- 現在のフレームでの各パラメータを得る -*/
fill_value_struct(values, m_frame);
int frame, intpart = 0;
int level_n = part_ports.size();
/*- 開始フレーム -*/
int startframe = (int)values.startpos_val;
float dpicorr = dpi * 0.01f, fractpart = 0;
/*- 不透明度の範囲(透明~不透明を 01 に正規化) -*/
float opacity_range =
(values.opacity_val.second - values.opacity_val.first) * 0.01f;
bool random_level = false;
bool isPrecomputingEnabled = false;
{
TRenderer renderer(TRenderer::instance());
isPrecomputingEnabled =
(renderer && renderer.isPrecomputingEnabled()) ? true : false;
}
/*- シュリンクしている場合 -*/
float dpicorr_shrinked;
if (values.unit_val == Iwa_TiledParticlesFx::UNIT_SMALL_INCH)
dpicorr_shrinked = dpicorr / shrink;
else
dpicorr_shrinked = dpi / shrink;
std::map<std::pair<int, int>, float> partScales;
/*- 現在のフレームをステップ値にする -*/
curr_frame = curr_frame / values.step_val;
Iwa_ParticlesManager *pc = Iwa_ParticlesManager::instance();
bool isFirstFrame = !(pc->isCached(fxId));
// Retrieve the last rolled frame
Iwa_ParticlesManager::FrameData *particlesData = pc->data(fxId);
std::list<Iwa_Particle> myParticles;
TRandom myRandom = m_parent->randseed_val->getValue();
values.random_val = &myRandom;
int totalparticles = 0;
/*- 規則正しく並んだ(まだ出発していない)粒子情報 -*/
QList<ParticleOrigin> particleOrigins;
/*- 出力画像のバウンディングボックス -*/
TRectD outTileBBox(tile->m_pos, TDimensionD(tile->getRaster()->getLx(),
tile->getRaster()->getLy()));
/*- 現在取っておいてあるデータのフレーム番号 -*/
int pcFrame = particlesData->m_frame;
/*- マージンをピクセル単位に換算する -*/
double pixelMargin;
{
TPointD vect(values.margin_val, 0.0);
TAffine aff(ri.m_affine);
aff.a13 = aff.a23 = 0;
vect = aff * vect;
pixelMargin = sqrt(vect.x * vect.x + vect.y * vect.y);
}
/*- 外側にマージンを取って粒子を生成 -*/
TRectD resourceTileBBox = outTileBBox.enlarge(pixelMargin);
/*- 初期粒子量。これが変わっていなければ、BGはそのまま描く -*/
int initialOriginsSize;
if (pcFrame > curr_frame) {
/*- データを初期化 -*/
// Clear stored particlesData
particlesData->clear();
pcFrame = particlesData->m_frame;
/*- まだ出発していない粒子情報を初期化 -*/
initParticleOrigins(resourceTileBBox, particleOrigins, curr_frame,
ri.m_affine, values, level_n, last_frame, pixelMargin);
initialOriginsSize = particleOrigins.size();
}
else if (pcFrame >= startframe - 1) {
myParticles = particlesData->m_particles;
myRandom = particlesData->m_random;
totalparticles = particlesData->m_totalParticles;
particleOrigins = particlesData->m_particleOrigins;
initialOriginsSize = -1;
} else {
/*- まだ出発していない粒子情報を初期化 -*/
initParticleOrigins(resourceTileBBox, particleOrigins, curr_frame,
ri.m_affine, values, level_n, last_frame, pixelMargin);
initialOriginsSize = particleOrigins.size();
}
/*- スタートからカレントフレームまでループ -*/
for (frame = startframe - 1; frame <= curr_frame; ++frame) {
/*- 参照画像はキャッシュされてるフレームでは必要ないのでは? -*/
if (frame <= pcFrame) continue;
int dist_frame = curr_frame - frame;
/*-
* ループ内の現在のフレームでのパラメータを取得。スタートが負ならフレーム=0のときの値を格納。
* -*/
fill_value_struct(values, frame < 0 ? 0 : frame * values.step_val);
/*- パラメータの正規化 -*/
normalize_values(values, ri);
/*- maxnum_valは"birth_rate"のパラメータ -*/
intpart = (int)values.maxnum_val;
/*-
* birth_rateが小数だったとき、各フレームの小数部分を足しこんだ結果の整数部分をintpartに渡す
* -*/
fractpart = fractpart + values.maxnum_val - intpart;
if ((int)fractpart) {
values.maxnum_val += (int)fractpart;
fractpart = fractpart - (int)fractpart;
}
std::map<int, TTile *> porttiles;
// Perform the roll
/*- RenderSettingsを複製して現在のフレームの計算用にする -*/
TRenderSettings riAux(ri);
/*- 64bitにする -*/
riAux.m_bpp = 64;
// riAux.m_bpp = 32;
int r_frame; // Useful in case of negative roll frames
if (frame < 0)
r_frame = 0;
else
r_frame = frame;
/*- コントロールに刺さっている各ポートについて -*/
for (std::map<int, TRasterFxPort *>::iterator it = ctrl_ports.begin();
it != ctrl_ports.end(); ++it) {
TTile *tmp;
/*- ポートが接続されていて、Fx内で実際に使用されていたら -*/
if ((it->second)->isConnected() && port_is_used(it->first, values)) {
TRectD bbox = resourceTileBBox;
/*- 素材が存在する場合、portTilesにコントロール画像タイルを格納 -*/
if (!bbox.isEmpty()) {
if (frame <= pcFrame) {
// This frame will not actually be rolled. However, it was
// dryComputed - so, declare the same here.
(*it->second)->dryCompute(bbox, r_frame, riAux);
} else {
tmp = new TTile;
if (isPrecomputingEnabled) {
(*it->second)
->allocateAndCompute(*tmp, bbox.getP00(),
convert(bbox).getSize(), 0, r_frame,
riAux);
} else {
std::string alias =
"CTRL: " + (*(it->second))->getAlias(r_frame, riAux);
TRasterImageP rimg = TImageCache::instance()->get(alias, false);
if (rimg) {
tmp->m_pos = bbox.getP00();
tmp->setRaster(rimg->getRaster());
} else {
(*it->second)
->allocateAndCompute(*tmp, bbox.getP00(),
convert(bbox).getSize(), 0, r_frame,
riAux);
}
}
porttiles[it->first] = tmp;
}
}
}
}
TTile baseImgTile;
if (values.base_ctrl_val &&
ctrl_ports.at(values.base_ctrl_val)->isConnected() &&
port_is_used(values.base_ctrl_val, values) &&
values.iw_rendermode_val !=
Iwa_TiledParticlesFx::
REND_ILLUMINATED) /*- 照明モードなら、BG素材は要らない -*/
{
std::string alias =
"BG_CTRL: " +
(*ctrl_ports.at(values.base_ctrl_val))->getAlias(r_frame, ri);
TRasterImageP rimg = TImageCache::instance()->get(alias, false);
if (rimg) {
baseImgTile.m_pos = tile->m_pos;
baseImgTile.setRaster(rimg->getRaster());
} else {
/*- 出力と同じbpcにする -*/
(*ctrl_ports.at(values.base_ctrl_val))
->allocateAndCompute(baseImgTile, tile->m_pos,
convert(resourceTileBBox).getSize(),
tile->getRaster(), r_frame, ri);
addRenderCache(alias, TRasterImageP(baseImgTile.getRaster()));
}
baseImgTile.getRaster()->lock();
}
// Invoke the actual rolling procedure
roll_particles(tile, porttiles, riAux, myParticles, values, 0, 0, frame,
curr_frame, level_n, &random_level, 1, last_frame,
totalparticles, particleOrigins,
intpart /*- 実際に生成したい粒子数 -*/
);
// Store the rolled data in the particles manager
if (!particlesData->m_calculated ||
particlesData->m_frame + particlesData->m_maxTrail < frame) {
particlesData->m_frame = frame;
particlesData->m_particles = myParticles;
particlesData->m_random = myRandom;
particlesData->buildMaxTrail();
particlesData->m_calculated = true;
particlesData->m_totalParticles = totalparticles;
particlesData->m_particleOrigins = particleOrigins;
}
// Render the particles if the distance from current frame is a trail
// multiple
/*- さしあたり、trailは無視する -*/
if (dist_frame == 0) {
//--------
// Store the maximum particle size before the do_render cycle
/*- 表示されている粒子のうち、各素材について最大サイズのものを記録しておく
条件にあわせ、飛んでいる粒子と飛び立つ前の粒子の両方で記録を行う
-*/
/*- ①飛んでいる粒子 -*/
if (values.iw_rendermode_val != Iwa_TiledParticlesFx::REND_BG) {
std::list<Iwa_Particle>::iterator pt;
for (pt = myParticles.begin(); pt != myParticles.end(); ++pt) {
Iwa_Particle &part = *pt;
int ndx = part.frame % last_frame[part.level];
std::pair<int, int> ndxPair(part.level, ndx);
std::map<std::pair<int, int>, float>::iterator it =
partScales.find(ndxPair);
if (it != partScales.end())
it->second = std::max(part.scale, it->second);
else
partScales[ndxPair] = part.scale;
}
}
/*- ②飛び立つ前の粒子がひとつでもあった場合、最大値でおきかえる -*/
if (values.iw_rendermode_val == Iwa_TiledParticlesFx::REND_ALL ||
values.iw_rendermode_val == Iwa_TiledParticlesFx::REND_BG) {
for (int lev = 0; lev < level_n; lev++) {
std::pair<int, int> ndxPair(lev, 0);
std::map<std::pair<int, int>, float>::iterator it =
partScales.find(ndxPair);
if (it != partScales.end())
it->second = std::max((float)values.scale_val.second, it->second);
else
partScales[ndxPair] = values.scale_val.second;
}
}
//--------
/*- ここで、出発した粒子の分、穴を開けた背景を描く -*/
/*- スイッチがONなら -*/
if (values.iw_rendermode_val == Iwa_TiledParticlesFx::REND_ALL ||
values.iw_rendermode_val == Iwa_TiledParticlesFx::REND_BG) {
/*- まだ粒子が飛び立っていない場合、そのまま背景を描く -*/
if (initialOriginsSize == particleOrigins.size()) {
TRop::resample(tile->getRaster(), baseImgTile.getRaster(), TAffine());
} else {
renderBackground(tile, particleOrigins, part_ports, ri, partLevel,
partScales, &baseImgTile);
}
}
/*- 粒子の描画。もし、背景モードなら描かない -*/
if (values.iw_rendermode_val != Iwa_TiledParticlesFx::REND_BG) {
if (values.toplayer_val == Iwa_TiledParticlesFx::TOP_SMALLER ||
values.toplayer_val == Iwa_TiledParticlesFx::TOP_BIGGER)
myParticles.sort(Iwa_ComparebySize());
if (values.toplayer_val == Iwa_TiledParticlesFx::TOP_SMALLER) {
int unit = 1 + (int)myParticles.size() / 100;
int count = 0;
std::list<Iwa_Particle>::iterator pt;
for (pt = myParticles.begin(); pt != myParticles.end(); ++pt) {
count++;
Iwa_Particle &part = *pt;
if (dist_frame <= part.trail && part.scale > 0.0f &&
part.lifetime > 0 &&
part.lifetime <=
part.genlifetime) // This last... shouldn't always be?
{
do_render(flash, &part, tile, part_ports, porttiles, ri, p_size,
p_offset, last_frame[part.level], partLevel, values,
opacity_range, dist_frame, partScales, &baseImgTile);
}
}
} else {
int unit = 1 + (int)myParticles.size() / 100;
int count = 0;
std::list<Iwa_Particle>::reverse_iterator pt;
for (pt = myParticles.rbegin(); pt != myParticles.rend(); ++pt) {
count++;
Iwa_Particle &part = *pt;
if (dist_frame <= part.trail && part.scale > 0.0f &&
part.lifetime > 0 &&
part.lifetime <= part.genlifetime) // Same here..?
{
do_render(flash, &part, tile, part_ports, porttiles, ri, p_size,
p_offset, last_frame[part.level], partLevel, values,
opacity_range, dist_frame, partScales, &baseImgTile);
}
}
}
}
/*- 粒子の描画 ここまで -*/
}
std::map<int, TTile *>::iterator it;
for (it = porttiles.begin(); it != porttiles.end(); ++it) delete it->second;
if (values.base_ctrl_val &&
ctrl_ports.at(values.base_ctrl_val)->isConnected() &&
port_is_used(values.base_ctrl_val, values) &&
values.iw_rendermode_val != Iwa_TiledParticlesFx::REND_ILLUMINATED)
baseImgTile.getRaster()->unlock();
}
}
/*-----------------------------------------------------------------
render_particles から来る
粒子の数だけ繰り返し
-----------------------------------------------------------------*/
void Iwa_Particles_Engine::do_render(
TFlash *flash, Iwa_Particle *part, TTile *tile,
std::vector<TRasterFxPort *> part_ports, std::map<int, TTile *> porttiles,
const TRenderSettings &ri, TDimension &p_size, TPointD &p_offset,
int lastframe, std::vector<TLevelP> partLevel,
struct particles_values &values, float opacity_range, int dist_frame,
std::map<std::pair<int, int>, float> &partScales, TTile *baseImgTile) {
/*- カメラに対してタテになっている粒子を描かずに飛ばす -*/
if (abs(cosf(part->flap_phi * 3.14159f / 180.0f)) < 0.03f) {
return;
}
// Retrieve the particle frame - that is, the *column frame* from which we are
// picking
// the particle to be rendered.
int ndx = part->frame % lastframe;
TRasterP tileRas(tile->getRaster());
std::string levelid;
double aim_angle = 0;
if (values.pathaim_val) {
float arctan = atan2f(part->vy, part->vx);
aim_angle = arctan * M_180_PI;
}
/*- 粒子の回転、スケールをアフィン行列に入れる -*/
// Calculate the rotational and scale components we have to apply on the
// particle
TRotation rotM(part->angle + aim_angle);
TScale scaleM(part->scale);
/*- ひらひら -*/
TAffine testAff;
float illuminant;
{
float theta = part->flap_theta * 3.14159f / 180.0f;
float phi = part->flap_phi * 3.14159f / 180.0f;
float cosT = cosf(theta);
float sinT = sinf(theta);
float k = 1.0f - cosf(phi);
testAff.a11 = 1.0f - k * cosT * cosT;
testAff.a21 = -k * cosT * sinT;
testAff.a12 = -k * cosT * sinT;
testAff.a22 = 1.0f - k * sinT * sinT;
/*- ここで、照明モードのとき、その明るさを計算する -*/
if (values.iw_rendermode_val == Iwa_TiledParticlesFx::REND_ILLUMINATED) {
float liTheta = values.iw_light_theta_val;
float liPhi = values.iw_light_phi_val;
float3 normVec = {sinf(theta) * sinf(phi), cosf(theta) * sinf(phi),
cosf(phi)};
float3 lightVec = {sinf(liTheta) * sinf(liPhi),
cosf(liTheta) * sinf(liPhi), cosf(liPhi)};
/*- 法線ベクトルと光源ベクトルの内積の絶対値 -*/
illuminant = abs(normVec.x * lightVec.x + normVec.y * lightVec.y +
normVec.z * lightVec.z);
}
}
TAffine M(rotM * scaleM * testAff);
// Particles deal with dpi affines on their own
TAffine scaleAff(m_parent->handledAffine(ri, m_frame));
float partScale =
scaleAff.a11 * partScales[std::pair<int, int>(part->level, ndx)];
TDimensionD partResolution(0, 0);
TRenderSettings riNew(ri);
// Retrieve the bounding box in the standard reference
TRectD bbox(-5.0, -5.0, 5.0, 5.0), standardRefBBox;
if (part->level <
(int)part_ports.size() && // Not the default levelless cases
part_ports[part->level]->isConnected()) {
TRenderSettings riIdentity(ri);
riIdentity.m_affine = TAffine();
(*part_ports[part->level])->getBBox(ndx, bbox, riIdentity);
// A particle's bbox MUST be finite. Gradients and such which have an
// infinite bbox
// are just NOT rendered.
// NOTE: No fx returns half-planes or similar (ie if any coordinate is
// either
// (std::numeric_limits<double>::max)() or its opposite, then the rect IS
// THE infiniteRectD)
if (bbox == TConsts::infiniteRectD) return;
}
// Now, these are the particle rendering specifications
bbox = bbox.enlarge(3);
standardRefBBox = bbox;
riNew.m_affine = TScale(partScale);
bbox = riNew.m_affine * bbox;
/*- 縮小済みのParticleのサイズ -*/
partResolution = TDimensionD(tceil(bbox.getLx()), tceil(bbox.getLy()));
if (flash) {
if (!partLevel[part->level]->frame(ndx)) {
if (part_ports[0]->isConnected()) {
TTile auxTile;
/*- テクスチャは出力タイルと同じbpcにする -*/
(*part_ports[0])
->allocateAndCompute(auxTile, p_offset, p_size, tile->getRaster(),
ndx, ri);
partLevel[part->level]->setFrame(ndx,
TRasterImageP(auxTile.getRaster()));
}
}
flash->pushMatrix();
const TAffine aff;
flash->multMatrix(scaleM * aff.place(0, 0, part->x, part->y));
{
TColorFader cf(TPixel32::Red, .5);
flash->draw(partLevel[part->level]->frame(ndx), &cf);
}
flash->popMatrix();
} else {
TRasterP ras;
std::string alias;
TRasterImageP rimg;
if (rimg = partLevel[part->level]->frame(ndx)) {
ras = rimg->getRaster();
} else {
alias = "PART: " + (*part_ports[part->level])->getAlias(ndx, riNew);
if (rimg = TImageCache::instance()->get(alias, false)) {
ras = rimg->getRaster();
// Check that the raster resolution is sufficient for our purposes
if (ras->getLx() < partResolution.lx ||
ras->getLy() < partResolution.ly)
ras = 0;
else
partResolution = TDimensionD(ras->getLx(), ras->getLy());
}
}
// We are interested in making the relation between scale and (integer)
// resolution
// bijective - since we shall cache by using resolution as a partial
// identification parameter.
// Therefore, we find the current bbox Lx and take a unique scale out of it.
partScale = partResolution.lx / standardRefBBox.getLx();
riNew.m_affine = TScale(partScale);
bbox = riNew.m_affine * standardRefBBox;
// If no image was retrieved from the cache (or it was not scaled enough),
// calculate it
if (!ras) {
TTile auxTile;
(*part_ports[part->level])
->allocateAndCompute(auxTile, bbox.getP00(),
TDimension(partResolution.lx, partResolution.ly),
tile->getRaster(), ndx, riNew);
ras = auxTile.getRaster();
// Finally, cache the particle
addRenderCache(alias, TRasterImageP(ras));
}
if (!ras) return; // At this point, it should never happen anyway...
// Deal with particle colors/opacity
TRasterP rfinalpart;
// TRaster32P rfinalpart;
double curr_opacity =
part->set_Opacity(porttiles, values, opacity_range, dist_frame);
if (curr_opacity != 1.0 || part->gencol.fadecol || part->fincol.fadecol ||
part->foutcol.fadecol) {
if (values.pick_color_for_every_frame_val && values.gencol_ctrl_val &&
(porttiles.find(values.gencol_ctrl_val) != porttiles.end()))
part->get_image_reference(porttiles[values.gencol_ctrl_val], values,
part->gencol.col);
rfinalpart = ras->clone();
part->modify_colors_and_opacity(values, curr_opacity, dist_frame,
rfinalpart);
} else
rfinalpart = ras;
TRasterP rfinalpart2;
/*- 照明モードのとき、その明るさを色に格納 -*/
if (values.iw_rendermode_val == Iwa_TiledParticlesFx::REND_ILLUMINATED) {
rfinalpart2 = rfinalpart->clone();
part->set_illuminated_colors(illuminant, rfinalpart2);
} else if (baseImgTile->getRaster() &&
!baseImgTile->getRaster()->isEmpty()) {
rfinalpart2 = rfinalpart->clone();
/*- サイズが小さい場合は、単に色を拾う -*/
if (partResolution.lx <= 4.0 && partResolution.ly <= 4.0)
part->get_base_image_color(baseImgTile, values, rfinalpart2, bbox, ri);
else
part->get_base_image_texture(baseImgTile, values, rfinalpart2, bbox,
ri);
} else
rfinalpart2 = rfinalpart;
// Now, let's build the particle transform before it is overed on the output
// tile
// First, complete the transform by adding the rotational and scale
// components from
// Particles parameters
M = ri.m_affine * M * TScale(1.0 / partScale);
// Then, retrieve the particle position in current reference.
TPointD pos(part->x, part->y);
pos = ri.m_affine * pos;
// Finally, add the translational component to the particle
// NOTE: p_offset is added to account for the particle relative position
// inside its level's bbox
M = TTranslation(pos - tile->m_pos) * M * TTranslation(bbox.getP00());
if (TRaster32P myras32 = tile->getRaster())
TRop::over(tileRas, rfinalpart2, M);
else if (TRaster64P myras64 = tile->getRaster())
TRop::over(tileRas, rfinalpart2, M);
else {
throw TException("ParticlesFx: unsupported Pixel Type");
}
}
}
/*-----------------------------------------------------------------*/
void Iwa_Particles_Engine::fill_array(
TTile *ctrl1, /*- ソース画像のTile -*/
int &regioncount, /*- 領域数を返す -*/
std::vector<int>
&myarray, /*- インデックスを返すと思われる。サイズはソースTileの縦横 -*/
std::vector<int> &lista,
std::vector<int> &listb, int threshold) {
int pr = 0;
int i, j;
int lx, ly;
lx = ctrl1->getRaster()->getLx();
ly = ctrl1->getRaster()->getLy();
/*prima riga*/
TRaster32P raster32 = ctrl1->getRaster();
raster32->lock();
TPixel32 *pix = raster32->pixels(0);
for (i = 0; i < lx; i++) {
if (pix->m > threshold) {
pr++;
if (!i) {
(regioncount)++;
myarray[i] = (regioncount);
} else {
if (myarray[i - 1]) myarray[i] = myarray[i - 1];
}
}
*pix++;
}
for (j = 1; j < ly; j++) {
for (i = 0, pix = raster32->pixels(j); i < lx; i++, pix++) {
/*TMSG_INFO("j=%d i=%d\n", j, i);*/
if (pix->m > threshold) {
std::vector<int> mask(4);
pr++;
/* l,ul,u,ur;*/
if (i) {
mask[0] = myarray[i - 1 + lx * j];
mask[1] = myarray[i - 1 + lx * (j - 1)];
}
if (i != lx - 1) mask[3] = myarray[i + 1 + lx * (j - 1)];
mask[2] = myarray[i + lx * (j - 1)];
if (!mask[0] && !mask[1] && !mask[2] && !mask[3]) {
(regioncount)++;
myarray[i + lx * j] = (regioncount);
} else {
int mc, firsttime = 1;
for (mc = 0; mc < 4; mc++) {
if (mask[mc]) {
if (firsttime) {
myarray[i + lx * j] = mask[mc];
firsttime = 0;
} else {
if (myarray[i + lx * j] != mask[mc]) {
lista.push_back(myarray[i + lx * j]);
listb.push_back(mask[mc]);
/*TMSG_INFO("j=%d i=%d mc=%d, mask=%d\n", j, i, mc,
* mask[mc]);*/
}
}
}
}
}
}
}
}
raster32->unlock();
}
/*-----------------------------------------------------------------*/
void Iwa_Particles_Engine::normalize_array(
std::vector<std::vector<TPointD>> &myregions, TPointD pos, int lx, int ly,
int regioncounter, std::vector<int> &myarray, std::vector<int> &lista,
std::vector<int> &listb, std::vector<int> & final) {
int i, j, k, l;
std::vector<int> tmp;
int maxregioncounter = 0;
int listsize = (int)lista.size();
// TMSG_INFO("regioncounter %d, eqcount=%d\n", regioncounter, eqcount);
for (k = 1; k <= regioncounter; k++) final[k] = k;
for (l = 0; l < listsize; l++) {
j = lista[l];
/*TMSG_INFO("j vale %d\n", j);*/
while (final[j] != j) j = final[j];
k = listb[l];
/*TMSG_INFO("k vale %d\n", k);*/
while (final[k] != k) k = final[k];
if (j != k) final[j] = k;
}
// TMSG_INFO("esco dal for\n");
for (j = 1; j <= regioncounter; j++)
while (final[j] != final[final[j]]) final[j] = final[final[j]];
/*conto quante cavolo di regioni sono*/
tmp.push_back(final[1]);
maxregioncounter = 1;
for (i = 2; i <= regioncounter; i++) {
int diff = 1;
for (j = 0; j < maxregioncounter; j++) {
if (tmp[j] == final[i]) {
diff = 0;
break;
}
}
if (diff) {
tmp.push_back(final[i]);
maxregioncounter++;
}
}
myregions.resize(maxregioncounter);
for (j = 0; j < ly; j++) {
for (i = 0; i < lx; i++) {
int tmpindex;
if (myarray[i + lx * j]) {
tmpindex = final[myarray[i + lx * j]];
/*TMSG_INFO("tmpindex=%d\n", tmpindex);*/
for (k = 0; k < maxregioncounter; k++) {
if (tmp[k] == tmpindex) break;
}
/*TMSG_INFO("k=%d\n", k);*/
TPointD tmppoint;
tmppoint.x = i;
tmppoint.y = j;
tmppoint += pos;
myregions[k].push_back(tmppoint);
}
}
}
}
/*-----------------------------------------------------------------*/
/*- multiがONのときのSource画像ctrl1の領域を分析 -*/
void Iwa_Particles_Engine::fill_subregions(
int cont_index, std::vector<std::vector<TPointD>> &myregions, TTile *ctrl1,
int thres) {
int regioncounter = 0;
int lx = ctrl1->getRaster()->getLx();
int ly = ctrl1->getRaster()->getLy();
std::vector<int> myarray(lx * ly);
std::vector<int> lista;
std::vector<int> listb;
fill_array(ctrl1, regioncounter, myarray, lista, listb, thres);
if (regioncounter) {
std::vector<int> final(regioncounter + 1);
normalize_array(myregions, ctrl1->m_pos, lx, ly, regioncounter, myarray,
lista, listb, final);
}
}
/*-----------------------------------------------------------------*/
/*- 入力画像のアルファ値に比例して発生濃度を変える 各Pointにウェイトを持たせる
* -*/
void Iwa_Particles_Engine::fill_single_region(
std::vector<TPointD> &myregions, TTile *ctrl1, int threshold,
bool do_source_gradation, QList<QList<int>> &myHistogram,
QList<ParticleOrigin> &particleOrigins) {
assert(ctrl1->getRaster());
TRaster32P raster32(ctrl1->getRaster()->getSize());
TRop::convert(raster32, ctrl1->getRaster());
assert(raster32); // per ora gestisco solo i Raster32
myregions.clear();
raster32->lock();
/*- 初期化 -*/
for (int i = 0; i < 256; i++) {
QList<int> tmpVec;
myHistogram.push_back(tmpVec);
}
if (!do_source_gradation) /*- 1階調の場合 -*/
{
for (int po = 0; po < particleOrigins.size(); po++) {
int index_x = (int)particleOrigins.at(po).pixPos[0];
int index_y = (int)particleOrigins.at(po).pixPos[1];
if (index_x < 0)
index_x = 0;
else if (index_x >= raster32->getLx())
index_x = raster32->getLx() - 1;
if (index_y < 0) {
index_y = 0;
continue;
} else if (index_y >= raster32->getLy()) {
index_y = raster32->getLy() - 1;
continue;
}
TPixel32 *pix = raster32->pixels(index_y);
pix += index_x;
if (pix->m > threshold) {
myHistogram[1].push_back(po);
myregions.push_back(TPointD(particleOrigins.at(po).pos[0],
particleOrigins.at(po).pos[1]));
}
}
}
else {
TRandom rand = TRandom(1);
for (int po = 0; po < particleOrigins.size(); po++) {
int index_x = (int)particleOrigins.at(po).pixPos[0];
int index_y = (int)particleOrigins.at(po).pixPos[1];
if (index_x < 0)
index_x = 0;
else if (index_x >= raster32->getLx())
index_x = raster32->getLx() - 1;
if (index_y < 0)
index_y = 0;
else if (index_y >= raster32->getLy())
index_y = raster32->getLy() - 1;
TPixel32 *pix = raster32->pixels(index_y);
pix += index_x;
if (pix->m > 0) {
/*- Histogramの登録 -*/
myHistogram[(int)pix->m].push_back(po);
myregions.push_back(TPointD(particleOrigins.at(po).pos[0],
particleOrigins.at(po).pos[1]));
}
}
}
raster32->unlock();
}
/*----------------------------------------------------------------
まだ出発していない粒子情報を初期化
----------------------------------------------------------------*/
bool potentialLessThan(const ParticleOrigin &po1, const ParticleOrigin &po2) {
return po1.potential < po2.potential;
}
void Iwa_Particles_Engine::initParticleOrigins(
TRectD &resourceTileBBox, QList<ParticleOrigin> &particleOrigins,
const double frame, const TAffine affine, struct particles_values &values,
int level_n, std::vector<int> &lastframe, /*- 素材カラムのフレーム長 -*/
double pixelMargin) {
/*- 敷き詰め三角形の一辺の長さをピクセル単位に換算する -*/
TPointD vect(values.iw_triangleSize, 0.0);
TAffine aff(affine);
aff.a13 = aff.a23 = 0;
vect = aff * vect;
double triPixSize = sqrt(vect.x * vect.x + vect.y * vect.y);
double pix2Inch = values.iw_triangleSize / triPixSize;
/*- 横方向の移動距離 -*/
double d_hori = values.iw_triangleSize * 0.5;
/*- 縦方向の移動距離 -*/
double d_vert = values.iw_triangleSize * 0.8660254;
/*- 正三角形を横に上下交互に並べたときの、中心位置のオフセット -*/
double vOffset = values.iw_triangleSize * 0.14433758;
/*- ピクセル位置の方も格納する -*/
double d_hori_pix = triPixSize * 0.5;
double d_vert_pix = triPixSize * 0.8660254;
double vOffset_pix = triPixSize * 0.14433758;
TRectD inchBBox(
resourceTileBBox.x0 * pix2Inch, resourceTileBBox.y0 * pix2Inch,
resourceTileBBox.x1 * pix2Inch, resourceTileBBox.y1 * pix2Inch);
/*- インチ位置のスタート -*/
double curr_y = inchBBox.y0;
/*- 行の1列目のタテのオフセット値 -*/
double startOff = -vOffset;
/*- ピクセル位置のスタート -*/
double curr_y_pix = 0.0;
double startOff_pix = -vOffset_pix;
/*- メモリの見積もり -*/
int gridSize;
{
int ySize = 0;
while (curr_y <= inchBBox.y1 + d_vert * 0.5) {
curr_y += d_vert;
ySize++;
}
int xSize = 0;
double curr_x = inchBBox.x0;
while (curr_x <= inchBBox.x1 + d_hori * 0.5) {
curr_x += d_hori;
xSize++;
}
gridSize = xSize * ySize;
}
curr_y = inchBBox.y0;
particleOrigins.reserve(gridSize);
/*- タテでループ -*/
while (curr_y <=
inchBBox.y1 +
d_vert *
0.5) /* ←d_vert * 0.5 は最後の一行を敷き詰めるための追加分 -*/
{
double curr_x = inchBBox.x0;
double off = startOff;
/*- 三角形が上下どっちを向いているかのフラグ -*/
bool isUp = (off < 0);
/*- ピクセル位置のスタート -*/
double curr_x_pix = 0.0;
double off_pix = startOff_pix;
/*- ヨコでループ -*/
while (curr_x <= inchBBox.x1 + d_hori * 0.5) {
unsigned char level =
(unsigned char)(values.random_val->getFloat() * level_n);
particleOrigins.append(ParticleOrigin(
curr_x, curr_y + off, values.random_val->getFloat(), isUp, level,
getInitSourceFrame(values, 0, lastframe[level]),
(short int)tround(curr_x_pix),
(short int)tround(curr_y_pix + off_pix)));
off = -off;
curr_x += d_hori;
isUp = !isUp;
off_pix = -off_pix;
curr_x_pix += d_hori_pix;
}
startOff = -startOff;
curr_y += d_vert;
startOff_pix = -startOff_pix;
curr_y_pix += d_vert_pix;
}
/*- 粒子をランダム値の大きい順に並べる -*/
qSort(particleOrigins.begin(), particleOrigins.end(), potentialLessThan);
}
//--------------------------------------------------
unsigned char Iwa_Particles_Engine::getInitSourceFrame(
const particles_values &values, int first, int last) {
switch (values.animation_val) {
case Iwa_TiledParticlesFx::ANIM_CYCLE:
case Iwa_TiledParticlesFx::ANIM_S_CYCLE:
return (unsigned char)first;
case Iwa_TiledParticlesFx::ANIM_SR_CYCLE:
return (unsigned char)(first +
(values.random_val->getFloat()) * (last - first));
default:
return (unsigned char)(first +
(values.random_val->getFloat()) * (last - first));
}
}
/*--------------------------------------------------
ここで、出発した粒子の分、穴を開けた背景を描く
--------------------------------------------------*/
void Iwa_Particles_Engine::renderBackground(
TTile *tile, QList<ParticleOrigin> &origins,
std::vector<TRasterFxPort *> part_ports, const TRenderSettings &ri,
std::vector<TLevelP> partLevel,
std::map<std::pair<int, int>, float> &partScales, TTile *baseImgTile) {
TRasterP tileRas(tile->getRaster());
int unit = 1 + (int)origins.size() / 100;
/*- まだ残っている粒子源について -*/
for (int po = 0; po < origins.size(); po++) {
ParticleOrigin origin = origins.at(po);
int ndx = origin.initSourceFrame;
/*- 粒子の回転、スケール -*/
TRotation rotM((origin.isUpward) ? 0.0 : 180.0);
TAffine M(rotM);
// Particles deal with dpi affines on their own
TAffine scaleAff(m_parent->handledAffine(ri, m_frame));
float partScale =
scaleAff.a11 * partScales[std::pair<int, int>(origin.level, ndx)];
TDimensionD partResolution(0, 0);
TRenderSettings riNew(ri);
// Retrieve the bounding box in the standard reference
TRectD bbox(-5.0, -5.0, 5.0, 5.0), standardRefBBox;
if (origin.level <
(int)part_ports.size() && // Not the default levelless cases
part_ports[origin.level]->isConnected()) {
TRenderSettings riIdentity(ri);
riIdentity.m_affine = TAffine();
(*part_ports[origin.level])->getBBox(ndx, bbox, riIdentity);
if (bbox == TConsts::infiniteRectD) return;
}
// Now, these are the particle rendering specifications
bbox = bbox.enlarge(3);
standardRefBBox = bbox;
riNew.m_affine = TScale(partScale);
bbox = riNew.m_affine * bbox;
/*- 縮小済みのParticleのサイズ -*/
partResolution = TDimensionD(tceil(bbox.getLx()), tceil(bbox.getLy()));
TRasterP ras;
std::string alias;
TRasterImageP rimg;
if (rimg = partLevel[origin.level]->frame(ndx)) {
ras = rimg->getRaster();
} else {
alias = "PART: " + (*part_ports[origin.level])->getAlias(ndx, riNew);
if (rimg = TImageCache::instance()->get(alias, false)) {
ras = rimg->getRaster();
// Check that the raster resolution is sufficient for our purposes
if (ras->getLx() < partResolution.lx ||
ras->getLy() < partResolution.ly)
ras = 0;
else
partResolution = TDimensionD(ras->getLx(), ras->getLy());
}
}
// We are interested in making the relation between scale and (integer)
// resolution
// bijective - since we shall cache by using resolution as a partial
// identification parameter.
// Therefore, we find the current bbox Lx and take a unique scale out of it.
partScale = partResolution.lx / standardRefBBox.getLx();
riNew.m_affine = TScale(partScale);
bbox = riNew.m_affine * standardRefBBox;
// If no image was retrieved from the cache (or it was not scaled enough),
// calculate it
if (!ras) {
TTile auxTile;
(*part_ports[origin.level])
->allocateAndCompute(auxTile, bbox.getP00(),
TDimension(partResolution.lx, partResolution.ly),
tile->getRaster(), ndx, riNew);
ras = auxTile.getRaster();
// Finally, cache the particle
addRenderCache(alias, TRasterImageP(ras));
}
if (!ras) return; // At this point, it should never happen anyway...
M = ri.m_affine * M * TScale(1.0 / partScale);
TPointD pos(origin.pos[0], origin.pos[1]);
pos = ri.m_affine * pos;
M = TTranslation(pos - tile->m_pos) * M * TTranslation(bbox.getP00());
if (TRaster32P myras32 = tile->getRaster())
TRop::over(tileRas, ras, M);
else if (TRaster64P myras64 = tile->getRaster())
TRop::over(tileRas, ras, M);
}
/*- サイズ縮める操作をいれる -*/
TRasterP resizedBGRas;
if (TRaster32P myBgRas32 = baseImgTile->getRaster())
resizedBGRas = TRaster32P(tileRas->getSize());
else if (TRaster64P myBgRas64 = baseImgTile->getRaster())
resizedBGRas = TRaster64P(tileRas->getSize());
else
return;
TAffine aff;
/*- リサンプル -*/
TRop::resample(resizedBGRas, baseImgTile->getRaster(), aff);
TRop::ropin(resizedBGRas, tileRas, tileRas);
std::cout << std::endl;
}