📄 ch19p1_particleemittertokenizer.cpp
字号:
#include "Ch19p1_ParticleEmitterTokenizer.h"
const int CParticleEmitterTokenizer::NUMBLENDINGMODES = 11;
const BLENDINGMODE CParticleEmitterTokenizer::m_BlendingModes[11] = {
{ "D3DBLEND_ZERO", D3DBLEND_ZERO },
{ "D3DBLEND_ONE", D3DBLEND_ONE },
{ "D3DBLEND_SRCCOLOR", D3DBLEND_SRCCOLOR },
{ "D3DBLEND_INVSRCCOLOR", D3DBLEND_INVSRCCOLOR },
{ "D3DBLEND_SRCALPHA", D3DBLEND_SRCALPHA },
{ "D3DBLEND_INVSRCALPHA", D3DBLEND_INVSRCALPHA },
{ "D3DBLEND_DESTALPHA", D3DBLEND_DESTALPHA },
{ "D3DBLEND_INVDESTALPHA", D3DBLEND_INVDESTALPHA },
{ "D3DBLEND_DESTCOLOR", D3DBLEND_DESTCOLOR },
{ "D3DBLEND_INVDESTCOLOR", D3DBLEND_INVDESTCOLOR },
{ "D3DBLEND_SRCALPHASAT", D3DBLEND_SRCALPHASAT }
};
bool CParticleEmitterTokenizer::ProcessNumber(CMinMax<float> &number, std::vector<CParticleEmitterToken>::iterator &TokenIter,
std::vector<CParticleEmitterToken>::iterator &EndIter)
{
// the first token is either the random keyword, or it's an actual number.
switch(TokenIter->m_Type) {
case KeywordRandom:
{
// parse random number into minmax
NextToken(TokenIter, EndIter);
if (TokenIter->m_Type != OpenParen) throw("Expecting opening paren after Random keyword.");
NextToken(TokenIter, EndIter);
if (TokenIter->m_Type != RealNumber) throw("Expecting first number within Random(...).");
number.m_Min = atof(TokenIter->m_strValue.c_str());
NextToken(TokenIter, EndIter);
if (TokenIter->m_Type != Comma) throw("Expecting comma within Random(...).");
NextToken(TokenIter, EndIter);
if (TokenIter->m_Type != RealNumber) throw("Expecting second number within Random(...).");
number.m_Max = atof(TokenIter->m_strValue.c_str());
NextToken(TokenIter, EndIter);
if (TokenIter->m_Type != CloseParen) throw("Missing close paren on Random(...).");
NextToken(TokenIter, EndIter);
}
break;
case RealNumber:
// min and max both equal realnumber
if (TokenIter->m_Type != RealNumber) throw("Expecting number.");
number.m_Max = number.m_Min = atof(TokenIter->m_strValue.c_str());
NextToken(TokenIter, EndIter);
break;
default: throw("Expecting either Random(...) or a number value.");
}
return(true);
}
bool CParticleEmitterTokenizer::ProcessVector(CMinMax<D3DXVECTOR3> &v,
std::vector<CParticleEmitterToken>::iterator &TokenIter,
std::vector<CParticleEmitterToken>::iterator &EndIter)
{
// this token needs to be a XYZ keyword.
if (TokenIter->m_Type != KeywordXYZ) throw("Expecting XYZ(...)!");
NextToken(TokenIter, EndIter);
if (TokenIter->m_Type != OpenParen) throw("Expecting ( after XYZ!");
CMinMax<float> x;
CMinMax<float> y;
CMinMax<float> z;
NextToken(TokenIter, EndIter); ProcessNumber(x, TokenIter, EndIter);
if (TokenIter->m_Type != Comma) throw("Vector components must be seperated by a comma.");
NextToken(TokenIter, EndIter); ProcessNumber(y, TokenIter, EndIter);
if (TokenIter->m_Type != Comma) throw("Vector components must be seperated by a comma.");
NextToken(TokenIter, EndIter); ProcessNumber(z, TokenIter, EndIter);
if (TokenIter->m_Type != CloseParen) throw("Expecting ) to close vector.");
NextToken(TokenIter, EndIter);
v.m_Min = D3DXVECTOR3(x.m_Min, y.m_Min, z.m_Min);
v.m_Max = D3DXVECTOR3(x.m_Max, y.m_Max, z.m_Max);
return(true);
}
bool CParticleEmitterTokenizer::ProcessColor(CMinMax<D3DXCOLOR> &c,
std::vector<CParticleEmitterToken>::iterator &TokenIter,
std::vector<CParticleEmitterToken>::iterator &EndIter)
{
// this token needs to be a RGBA keyword.
if (TokenIter->m_Type != KeywordColor)
throw("Expecting RGBA(...)!");
NextToken(TokenIter, EndIter);
if (TokenIter->m_Type != OpenParen) throw("Expecting ( after RGBA!");
CMinMax<float> r;
CMinMax<float> g;
CMinMax<float> b;
CMinMax<float> a;
NextToken(TokenIter, EndIter); ProcessNumber(r, TokenIter, EndIter);
if (TokenIter->m_Type != Comma) throw("Color components must be seperated by a comma.");
NextToken(TokenIter, EndIter); ProcessNumber(g, TokenIter, EndIter);
if (TokenIter->m_Type != Comma) throw("Color components must be seperated by a comma.");
NextToken(TokenIter, EndIter); ProcessNumber(b, TokenIter, EndIter);
if (TokenIter->m_Type != Comma) throw("Color components must be seperated by a comma.");
NextToken(TokenIter, EndIter); ProcessNumber(a, TokenIter, EndIter);
if (TokenIter->m_Type != CloseParen) throw("Expecting ) to close vector.");
NextToken(TokenIter, EndIter);
c.m_Min = D3DXCOLOR(r.m_Min, g.m_Min, b.m_Min, a.m_Min);
c.m_Max = D3DXCOLOR(r.m_Max, g.m_Max, b.m_Max, a.m_Max);
return(true);
}
bool CParticleEmitterTokenizer::ProcessAlphaBlendMode(int &alphablendmode,
std::vector<CParticleEmitterToken>::iterator &TokenIter,
std::vector<CParticleEmitterToken>::iterator &EndIter)
{
if (TokenIter->m_Type != AlphaBlendMode) throw("Expecting alpha blend mode (D3DBLEND_ZERO, D3DBLEND_ONE, etc.).");
for (int q=0; q < NUMBLENDINGMODES; q++) {
// careful here... must match on both string and size
// (i.e., to differentiate between D3DBLEND_SRCALPHA and D3DBLEND_SRCALPHASAT).
if (TokenIter->m_strValue.find(m_BlendingModes[q].name) != std::string::npos &&
TokenIter->m_strValue.size() == strlen(m_BlendingModes[q].name)) {
alphablendmode = m_BlendingModes[q].mode;
NextToken(TokenIter, EndIter);
return(true);
}
}
throw("Invalid alpha blending mode!");
return(false);
}
bool CParticleEmitterTokenizer::ProcessTime(CMinMax<float> &TimeRange, bool &IsFade, const float InitialTime, const float FinalTime,
std::vector<CParticleEmitterToken>::iterator &TokenIter,
std::vector<CParticleEmitterToken>::iterator &EndIter)
{
// determine if this time specification is a fade directive
if (TokenIter->m_Type == KeywordFade) {
// it is... the next token must be "so"
NextToken(TokenIter, EndIter); if (TokenIter->m_Type != KeywordSo) throw("Expecting \"so\" after \"fade\".");
// flip the fade flag on
IsFade = true;
// move to next token (so that we land on "at" for the code below)
NextToken(TokenIter, EndIter);
}
else IsFade = false; // just to be safe
switch(TokenIter->m_Type) {
case KeywordAt:
{
// easy, just grab the time
NextToken(TokenIter, EndIter);
ProcessNumber(TimeRange, TokenIter, EndIter);
}
break;
case KeywordInitial:
{
// use initial time that was passed in.
if (IsFade) throw("Cannot use \"fade so\" modifier on \"initial\" times.");
TimeRange.m_Min = InitialTime;
TimeRange.m_Max = InitialTime;
CParticleEmitterTokenizer::NextToken(TokenIter, EndIter);
}
break;
case KeywordFinal:
{
// use final time that was passed in.
TimeRange.m_Min = FinalTime;
TimeRange.m_Max = FinalTime;
CParticleEmitterTokenizer::NextToken(TokenIter, EndIter);
}
break;
default: throw("Expected time specifier: \"[fade so] at\", \"initial\", or \"[fade so] final\"");
}
return(true);
}
void CParticleEmitterTokenizer::DetermineTokenType(CParticleEmitterToken &token)
{
token.m_Type = Unknown;
// these things are easy to see...
if (isdigit(token.m_strValue[0]) || token.m_strValue[0] == '-') { token.m_Type = RealNumber; return; }
if (token.m_strValue[0] == '=') { token.m_Type = Equals; return; }
if (token.m_strValue[0] == ',') { token.m_Type = Comma; return; }
if (token.m_strValue[0] == '(') { token.m_Type = OpenParen; return; }
if (token.m_strValue[0] == ')') { token.m_Type = CloseParen; return; }
if (token.m_strValue[0] == '{') { token.m_Type = OpenBrace; return; }
if (token.m_strValue[0] == '}') { token.m_Type = CloseBrace; return; }
if (token.m_strValue[0] == '\"') { token.m_Type = Quote; return; }
// if we got here, it's not a quote... so convert it to uppercase.
MakeUpperCase(token.m_strValue);
// keywords are easy to figure out, just look for the text...
if (token.m_strValue.find("PARTICLESYSTEM") != std::string::npos) { token.m_Type = KeywordParticleSystem; return; }
if (token.m_strValue.find("EVENTSEQUENCE") != std::string::npos) { token.m_Type = KeywordEventSequence; return; }
if (token.m_strValue.find("RANDOM") != std::string::npos) { token.m_Type = KeywordRandom; return; }
if (token.m_strValue.find("XYZ") != std::string::npos) { token.m_Type = KeywordXYZ; return; }
if (token.m_strValue.find("RGBA") != std::string::npos) { token.m_Type = KeywordColor; return; }
if (token.m_strValue.find("FADE") != std::string::npos) { token.m_Type = KeywordFade; return; }
if (token.m_strValue.find("INITIAL") != std::string::npos) { token.m_Type = KeywordInitial; return; }
if (token.m_strValue.find("FINAL") != std::string::npos) { token.m_Type = KeywordFinal; return; }
if (token.m_strValue.find("TEXTURE") != std::string::npos) { token.m_Type = KeywordTexture; return; }
// these two keywords are embedded in other words, so we've got to be careful.
if (token.m_strValue.find("SO") != std::string::npos && token.m_strValue.size() == 2) { token.m_Type = KeywordSo; return; }
if (token.m_strValue.find("AT") != std::string::npos && token.m_strValue.size() == 2) { token.m_Type = KeywordAt; return; }
// now it gets harder...
if (token.m_strValue.find("D3DBLEND_") != std::string::npos) { token.m_Type = AlphaBlendMode; return; }
if (token.m_strValue.find("SOURCEBLENDMODE") != std::string::npos ||
token.m_strValue.find("DESTBLENDMODE") != std::string::npos) {
token.m_Type = SeqAlphaBlendModeProp; return;
}
if (token.m_strValue.find("LIFETIME") != std::string::npos ||
token.m_strValue.find("EMITRATE") != std::string::npos ||
token.m_strValue.find("NUMPARTICLES") != std::string::npos ||
token.m_strValue.find("LOOPS") != std::string::npos) {
token.m_Type = SeqNumericProp; return;
}
if (token.m_strValue.find("SPAWNDIR") != std::string::npos ||
token.m_strValue.find("GRAVITY") != std::string::npos ||
token.m_strValue.find("EMITRADIUS") != std::string::npos) {
token.m_Type = SeqVectorProp; return;
}
if (token.m_strValue.find("POSITION") != std::string::npos) {
token.m_Type = SysVectorProp; return;
}
if (token.m_strValue.find("SIZE") != std::string::npos ||
token.m_strValue.find("EVENTTIMER") != std::string::npos) {
token.m_Type = ParticleNumericProp; return;
}
if (token.m_strValue.find("VELOCITY") != std::string::npos) {
token.m_Type = ParticleVectorProp; return;
}
if (token.m_strValue.find("COLOR") != std::string::npos) {
token.m_Type = ParticleColorProp; return;
}
}
void CParticleEmitterTokenizer::AddToken(CParticleEmitterToken token)
{
if (token.m_strValue.size()) {
DetermineTokenType(token);
m_TokenVector.push_back(token);
}
}
void CParticleEmitterTokenizer::Tokenize(const char *str)
{
enum CurState {
InWhiteSpace = 1,
InText,
InQuote,
InComment
};
CurState cs = InWhiteSpace;
const char *p = str;
CParticleEmitterToken token;
while ((*p) != 0) {
switch(cs) {
case InWhiteSpace:
{
// if this letter is not whitespace,
if (!isspace(*p)) {
// add it to the running buffer
token.m_strValue = (*p);
// switch to appropriate case
if ((*p) == '/' && *(p+1) == '/') cs = InComment;
else cs = (*p == '\"') ? InQuote : InText;
} // end if
}
break;
case InText:
{
// if this letter is whitespace
if (isspace(*p)) {
// add the completed token to the vector
AddToken(token);
// switch to whitespace case
cs = InWhiteSpace;
} else {
// if this letter is a token terminator
if ((*p) == '(' || (*p) == ')' || (*p) == ',' || (*p) == '\"' || (*p) == '{' || (*p) == '}' || (*p) == '/') {
if ((*p) == '/' && *(p+1) == '/') cs = InComment;
else {
// add the completed token to the vector
DetermineTokenType(token);
AddToken(token);
// if it was a quote, transition to InQuote state
if ((*p) == '\"') cs = InQuote;
// otherwise, process this one char as a token
else {
token.m_strValue = (*p);
AddToken(token);
token.m_strValue = "";
}
}
} else {
// add this letter to the work in progress token
token.m_strValue += (*p);
} // end if
} // end if
}
break;
case InComment:
{
// C++ style comments - skip everything until end of line
if (*p == '\n') { token.m_strValue = ""; cs = InWhiteSpace; }
}
break;
case InQuote:
{
// unconditionally add this letter to the token until we hit a close quote
token.m_strValue += (*p);
if (*p == '\"') {
AddToken(token);
// go back to whitespace
cs = InWhiteSpace;
}
}
break;
}
p++;
}
AddToken(token);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -