You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

206 lines
5.8 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "HeadSpin.h"
#include "Max.h"
#include "../MaxMain/plMaxNode.h"
#include "plRenderGlobalContext.h"
plRenderGlobalContext::plRenderGlobalContext(Interface* ip, TimeValue t)
{
fInterface = ip;
renderer = ip->GetProductionRenderer();
projType = PROJ_PERSPECTIVE;
devWidth = 640;
devHeight = 480;
xscale = 1.6f;
yscale = -1.6f;
xc = 320.f;
yc = 240.f;
antialias = 1;
camToWorld.IdentityMatrix();
worldToCam.IdentityMatrix();
nearRange = 0;
farRange = 10000.f;
devAspect = 1.f; // PIXEL aspect ratio of device pixel H/W
frameDur = 1.f;
envMap = nil;
globalLightLevel.White();
atmos = nil;
pToneOp = nil; // The tone operator, may be NULL
time = t;
wireMode = false; // wire frame render mode?
wire_thick = 1.f; // global wire thickness
force2Side = false; // is force two-sided rendering enabled
inMtlEdit = false; // rendering in mtl editor?
fieldRender = false; // are we rendering fields
first_field = 1; // is this the first field or the second?
field_order = 1; // which field is first: 0->even first, 1->odd first
objMotBlur = true; // is object motion blur enabled
nBlurFrames = 10; // number of object motion blur time slices
SetRenderElementMgr(ip->GetRenderElementMgr(RS_Production));
}
plRenderGlobalContext::~plRenderGlobalContext()
{
int i;
for( i = 0; i < fInstList.GetCount(); i++ )
{
fInstList[i].Cleanup();
}
}
void plRenderGlobalContext::Update(TimeValue t)
{
time = t;
int i;
for( i = 0; i < fInstList.GetCount(); i++ )
fInstList[i].Update(time);
}
void plRenderGlobalContext::MakeRenderInstances(plMaxNode* root, TimeValue t)
{
time = t;
int i;
for( i = 0; i < root->NumberOfChildren(); i++ )
IMakeRenderInstances((plMaxNode*)root->GetChildNode(i), t, false);
for( i = 0; i < fInstList.GetCount() - 1; i++ )
fInstList[i].SetNext(&fInstList[i+1]);
}
void plRenderGlobalContext::IMakeRenderInstances(plMaxNode* node, TimeValue t, hsBool isBarney)
{
const char* dbgNodeName = node->GetName();
if( !isBarney )
isBarney = node->GetIsBarney();
hsBool doMe = isBarney || (node->CanConvert() && node->GetDrawable());
if( !doMe )
return;
int idx = fInstList.GetCount();
plRenderInstance* inst = fInstList.Push();
if( !inst->GetFromNode(node, t, idx) )
fInstList.Pop();
int i;
for( i = 0; i < node->NumberOfChildren(); i++ )
IMakeRenderInstances((plMaxNode *)node->GetChildNode(i), t, isBarney);
}
void plRenderGlobalContext::IntersectRay(RenderInstance *inst, Ray& origRay, ISect &isct, ISectList &xplist, BOOL findExit)
{
const float kFarAway = 1.e5f;
Ray ray;
if( findExit )
{
ray.p = origRay.p + origRay.dir * kFarAway;
ray.dir = -ray.dir;
}
else
{
ray = origRay;
}
float at;
Point3 norm;
DWORD faceIdx;
Point3 bary;
int hit = inst->mesh->IntersectRay(ray, at, norm, faceIdx, bary);
if( hit )
{
ISect thisHit;
thisHit.t = findExit ? kFarAway - at : at;
thisHit.exit = findExit;
thisHit.backFace = findExit;
thisHit.inst = inst;
thisHit.fnum = faceIdx;
thisHit.bc = bary;
Point3 worldP = (ray.p + at * ray.dir);
thisHit.p = inst->GetINode()->GetObjectTM(time) * worldP;
thisHit.pc = worldToCam * thisHit.p;
thisHit.mtlNum = inst->mesh->faces[faceIdx].getMatID();
Mtl* mtl = inst->GetINode()->GetMtl();
thisHit.matreq = mtl ? mtl->Requirements(thisHit.mtlNum) : 0;
thisHit.next = nil;
if( thisHit.matreq & (MTLREQ_TRANSP | MTLREQ_ADDITIVE_TRANSP) )
{
// advance the ray and try again. This one goes in the xplist.
ISect* xHit = GetNewISect();
*xHit = thisHit;
xplist.Add(xHit);
const float kAdvanceHack = 0.5f;
Ray newRay;
newRay.p = origRay.p + origRay.dir * (thisHit.t + kAdvanceHack);
newRay.dir = origRay.dir;
IntersectRay(inst, newRay, isct, xplist, findExit);
}
else
{
xplist.Prune(thisHit.t);
isct = thisHit;
}
}
}
BOOL plRenderGlobalContext::IntersectWorld(Ray &ray, int skipID, ISect &hit, ISectList &xplist, int blurFrame)
{
hit.t = -1.f;
xplist.Init();
int i;
for( i = 0; i < fInstList.GetCount(); i++ )
{
if( skipID != i )
{
ISect thisHit;
hit.t = -1.f;
IntersectRay(&fInstList[i], ray, thisHit, xplist, false);
if( thisHit.t >= 0 )
{
if( (hit.t < 0) || (thisHit.t < hit.t) )
{
// grab our new winner.
hit = thisHit;
}
}
}
}
return (hit.t >= 0) || !xplist.IsEmpty();
}