Take full control of MME Effects by editing .fx files
So, you’ve downloaded your new shiny Beamman’s so-and-so effect, and now you eagerly attempt to use it in your presentation. You attach it to a Dummy Bone, move it around, play with its Si and Tr settings… but for some reason it’s not exactly what you expected. It surely looked more flashy on NikoNiko. Somehow you feel that you don’t unseal its full potential.
Of course, this is most probably true.
Each effect has lots of parts in its script that can be tinkered with, allowing it more flexibility. Earlier we learned to use accessory’s settings in order to setup some of effect’s internal parameters from AMP panel. This tool is limited, though – it has a finite amount of fields that can be used, and more often than not a bigger part of them can’t be used because messing with accessory’s position and rotation fields may shift the visual effect in an undesirable way. Fortunately, there’s a way to have as much control over .fx’ parameters as you desire.
Look what’s inside
Right at the beginning of the script, you can see the following code:
// パーティクル数(0~1024) #define PARTICLE_COUNT 1024 //生成制 float CutSpeed = 0; //色 //フィルライト色 float3 ParticleColor < string UIName = "ParticleColor"; string UIWidget = "Color"; bool UIVisible = true; > = float3(0.1,0.5,1); //バラけ角度 float particleSpread < string UIName = "particleSpread"; string UIWidget = "Slider"; bool UIVisible = true; float UIMin = 0; float UIMax = 360; > = 120; //再生スピード float particleSpeed < string UIName = "particleSpeed"; string UIWidget = "Slider"; bool UIVisible = true; float UIMin = 0; float UIMax = 10; > = 5;
There are many more definitions like these in this script, but you get the picture. Most usually, effects start with a block of definitions for main parameters that the effect uses, and these numbers can be modified to change way it looks without harm to functionality.
Lines starting with “#define” we will ignore for now. While they can be modified by editing the effect as a text file, it’s impossible (or, at least, inadvisable, for effect’s author probably had reason to declare them this way) to transform them into full-fledged controls, which is our goal. The rest, though, is more interesting.
Burn, baby, burn
Absolute power
Let’s once again revise the CONTROLOBJECT operator we once mastered. In the previous article, we learned to use it like this:
float RudderSi : CONTROLOBJECT < string name = “(self)”; string item = “Si”; >;
The “name” parameter here refers currently to the instance of a body the .fx script is attached to. And, as we used to attach effects to accessories, the operator only has access to properties of an accessory (in this case, the Size parameter that is provided by AMP), which are few. By setting the name of a *different* object, though, we can inspect that other object’s properties as well. And, unlike accessories, .pmd models have unlimited amount of elements whose values can be accessed and used as a variable in the script. For an effect with lots of parameters this can be a big advantage. We can create a dummy .pmd model, add some named elements to it, and then refer to their names as to our script’s controls.
Now let’s fix our script. First, we’ll take a simple variable “particleSpeed” and give it a control. Instead of the original description
float particleSpeed <
string UIName = “particleSpeed”;
string UIWidget = “Slider”;
bool UIVisible = true;
float UIMin = 0;
float UIMax = 10;
> = 5;
we define it like this:
float particleSpeed : CONTROLOBJECT < string name = “burnerControl.pmd”; string item = “Speed”; >;
EDITOR’S NOTE: The WordPress engine used by LearnMMD site edits the posted text as it finds necessary; I can not control it. In particular, it changes straight ASCII double quotes into Unicode curved ones that MME script interpreter does not recognize. If you want to copypaste code fragments from this page into your own script, make sure to fix them back. |
What does it mean? When seeing such a command, the script interpreter assumes that a) somewhere among models loaded into MMD there’s a model named “burnerControl.pmd”, and b) among its bones or morphs (aka “facials”) there’s one named “Speed” (we could give it the same name as the variable, but you’d want to see more comprehensible titles when you’re working with your effect in MMD). If it were a bone, values that that could be read from it would be its coordinates and/or rotation angles. We’ll stick to morphs, though, as MMD provides convenient sliders for them in the Facial Manipulation Panel. So: if we have a “Speed” morph in a “burnerControl” model, its slider position will be scanned in real time and instantly assigned to our “particleSpeed” variable.
There’s a caveat, though. From the description of “particleSpeed” in our source script we can assume that it’s supposed to be able to have values in the interval from 0 to 10 (this is actually why I called this effect “friendly”. In scripts that only give us short definitions of variables, we have to *guess* what values they’re allowed to take). And the value returned from a morph slider can only be between 0.0 and 1.0. So we’ll have to find where this variable is used *inside* the script…
…fortunately, there’s just one such line…
float t = frac(Pos.z + particleSpeed * time_0_X);
…and modify it to recalculate the value gotten from the control element:
float t = frac(Pos.z + particleSpeed * time_0_X * 10);
The balance is restored now. Don’t forget to remove the original description of particleSpeed from the beginning part.
One more thing before we move to other variables. For the sake of obviousness, the name of a control object was embedded in our example above as a literal value. It is okay if we only use it once, but since our object will be used to hold a lot of controls (and because it’s considered good manners :D ), it’s preferable to define it as a separate parameter. So we’ll modify our definition a bit:
#define myPmd “burnerControl.pmd”
float particleSpeed : CONTROLOBJECT < string name = myPmd; string item = “Speed”; >;
Next step – “particleSize” variable. The procedure is the same: replace the original definition with the one using the control model (I’ll omit this step for the rest of variables, as it is the same):
float particleSpeed : CONTROLOBJECT < string name = myPmd; string item = “Size“; >;
Then find the place it is used… what’s there?
Base.xyz += (Pos.x * 10)*Side*0.1*particleSize;
Looks like the author already added some compensating factor for a variable to make it accept more visually pleasing numbers in the header. All we need is simply to remove it:
Base.xyz += (Pos.x * 10)*Side*particleSize;
Now for “particleSpread”:
particleSpread = particleSpread * 3.14159265/180.0;
particleSpread *= 0.5;
Having in mind that the limit for this variable is set to 360 in the original description, I hope you aren’t confused by these numbers. All we need is, again, to remove unnecessary multipliers:
particleSpread = particleSpread * 3.14159265;
And two other variables are actually used in one place, so we’ll take care of them both:
len = (len*(MaxMoveSpd-MinMoveSpd))+MinMoveSpd;
ret_mat[3].z = -len*t * 10;
There’s also a bunch of variables like “particleSize2”, “particleSpread2” etc. The only place they are mentioned is here:
if((idx % 2) == 0)
{
MinMoveSpd = MinMoveSpd2;
MaxMoveSpd = MaxMoveSpd2;
particleSpread = particleSpread2;
particleSize = particleSize2;
}
All right, this might be a bit confusing if you’re not a programmer, and not really typical to many effects. Did I mention that the Burner consists of two *identical* sheafs of particles? Well, except for numerical parameters that describe their respective shapes, they are. What the code above does is allow to re-use the rest of the procedure twice, one time for a first sheaf, and then for a second, simply by substituting its variables. So we’ll have to create control gauges for these four variables as well, but won’t do anything else about them – they are already taken care of!
Now the remaining “ParticleColor” variable is a bit different. It is declared as a float3, a composite data type consisting of three numbers (for R, G, and B components). HLSL, the programming language designed specifically for creating shaders (and the one MME effects are written with), has native support for handling such peculiar data types used in graphical calculations. Morphs, though, can return only a single number (technically, we could create a bone for handling this particular variable, but that would be more inconvenient for multiple reasons). So, we’ll create three controls instead of one:
float PcolorRed : CONTROLOBJECT < string name = myPmd; string item = “ColorRed”; >;
float PcolorGreen : CONTROLOBJECT < string name = myPmd; string item = “ColorGreen”; >;
float PcolorBlue : CONTROLOBJECT < string name = myPmd; string item = “ColorBlue”; >;
…and change the line that uses ParticleColor accordingly: from
col.rgb *= ParticleColor;
to
col.rgb *= float3(PcolorRed, PcolorGreen, PcolorBlue);
That covers changes that we need to do to the script to make it listen to our sliders. Save the script under a different name, like, “burnerPro.fx” and make a copy of “burner.x” named “burnerPro.x” as well.
Mission control
Time to prepare a steering wheel for our effect. Launch PMDE and start a new model. Create a Primitive (no matter which one) and make it invisible by collapsing its size to zero. It won’t matter for the actual effect, you’ll only need it as a placeholder to create morphs.
Congratulations, your model just flashed its first smile at you :P.
Close the morph window, you won’t need it anymore. Now just right-click on the new morph in the list and select “Replication” a dozen times – you will need a lot of controls. Start renaming them right here in the list. Don’t forget to rename both English and Japanese fields – you will look at the former when working with MMD, but it is the latter the MME engine is looking for. Here’s the full list of morphs you need to have (make sure you defined controls for each of them in the script):
ColorRed
ColorGreen
ColorBlue
SpeedSize
Spread
MinMoveSpd
MaxMoveSpdSize2
Spread2
MinMoveSpd2
MaxMoveSpd2
They should match values of “string item” parameters of your definitions rather than respective variable names used it the script. Also, you can distribute morphs to groups (eye, brow, lips, other) to as you find it comfortable. Fill the “Model name” tag to remember what the model does when seeing it in the list, and save the model as “burnerControl.pmd”.
Now launch MMD. Load the accessory “burnerPro.x” into AMP, then the model “burnerControl.pmd”. You won’t see any effect at first, because unlike the standard Burner, its parameters are not initialized to any significant values. You’ll have to give its control morphs some values, and then…
Do your own magic
So, what is the lesson of today’s exercise? Is it about how to get a new and improved Beamman’s Burner? I should certainly hope not. The real point is, lots of effects can be tuned simply by altering some numbers in their headers, changing their appearance and making them look more closely to what you *want* to see rather than what you get simply by using the effect as is. And doing so with a real-time tool rather than fiddling with the script manually not only makes finding your “look” easier, but also allows you to provide a completely new quality to the effect: change its looks on the fly, producing dynamic visuals not possible with a “static” effect (not to mention having a single effect for all videos that use it rather than producing and cataloging each variety you may need). While making a MME effect on your own may look difficult and beyond your skill, you don’t actually have to be a programmer or know a single program language in order to do necessary changes for an existing one. Once you learn few simple patterns and attain understanding of what you’re doing and why, many effects that used to have a fixed view will bloom with new colors. Remember: for a really inquisitive mind, sky is *not* the limit!
Models used in illustrations: Chibi Len and Meiko by MMD-francis-co, Classroom interior by MMDFakewings18, the gizmo they are looking at is Ghost Trap from GhostBusters Pack by OniMau619. The final model is a combination of Nakao base and Office outfit by 2234083174 (recolored), and hair by MMDFakewings18. Half-Lambert shader by kyaami is used for illumination.
Bonus level: Variety of unity
As you could see, the improved Burner effect is loaded into MMD with an accessory, just like the regular one, and then looks for a side element – a .pmd model – for instructions. That’s not the only way to do it, though. For example, Beamman’s BeamCharge (which uses .pmd-based control elements in its original form) has the script working directly with the model. The difference from the process described above is:
- you have the model (rather than accessory) with the same name as the script, which causes MME to load the latter automatically.
- in CONTROLOBJECT’s definition, you refer to it as string name = “(self)”; thus making the engine to look for necessary control elements in script’s “own” object.
- instead of attaching the accessory to a Dummy Bone, as you do with most other effects that need positioning, you move the model itself by its Center bone.
In such case, you exert control over each instance of loaded effect individually, whereas the control object described in the article sets parameters to *all* Burners you have loaded into MMD at the same time. (unfortunately, in Burner’s case due to the way it’s written model’s position is calculated differently from accessorie’s, so trying to make a .pmd-only version of the Burner produces a serious mess. Trying to fix it goes far beyond the scope of this article)
As a different example, there’s a series of effects by HariganeP: ActiveParticle, ActiveParticleSmoke, ActiveParticleFire, ActiveParticleLight, JetSmoke, etc. Each of them has an additional set of accessories: BackgroundControl.x, TimeControl.x, and SmoothControl.x. They serve basically the same purpose as the .pmd control unit described above (except that each of them controls only one aspect of an effect, whereas the model approach allows to gather all controls under the same hood). What is interesting, though, is that one loaded instance of an accessory controls not just all instances of an effect, but *every* effect in the series that supports it. That is, you may fill your scene with ActiveParticleFire, ActiveParticleSmoke, and JetSmoke, and then setting “Tr” to zero in TimeControl.x stops action for the whole scene at once.
In the next episode: Care to dance? Use the technique you’ve just learned in collaboration with external programs like VMDSpectrum Analyzer to enhance your effects even further!
– SEE BELOW for MORE MMD TUTORIALS…
— — —
– _ — –