Example #1
0
LTBOOL CWeatherFX::Update()
{
	// Make sure we're supposed to be here...

    if (m_bWantRemove || !m_hServerObject) return LTFALSE;


	// Determine what type of surface this brush is on top of.

	if (m_bFirstUpdate)
	{
        m_bFirstUpdate = LTFALSE;

		ClientIntersectQuery iQuery;
		ClientIntersectInfo  iInfo;

		iQuery.m_Flags	 = INTERSECT_OBJECTS | INTERSECT_HPOLY | IGNORE_NONSOLID;
		iQuery.m_From    = m_vPos;

		iQuery.m_To   = m_vPos;
		iQuery.m_To.y = m_fFloorY - 100;

		if (m_pClientDE->IntersectSegment(&iQuery, &iInfo))
		{
			m_eSurfaceType = GetSurfaceType(iInfo);

			// Create splash sprites if necessary...

			if (m_dwFlags & WFLAG_RAIN)
			{
				CreateSplashSprites();
			}
		}
	}


	// Update Snow...

	if (m_dwFlags & WFLAG_SNOW)
	{
		HOBJECT hObj = m_Snow.GetObject();
		if (hObj)
		{
			bool bEnable = GetConsoleInt("EnableWeatherFX",1) != 0;
			if (bEnable)
			{
				m_Snow.Update();
			}
			g_pCommonLT->SetObjectFlags(hObj, OFT_Flags, bEnable ? FLAG_VISIBLE : 0, FLAG_VISIBLE);
		}
	}


	// Update Rain...

	if (m_dwFlags & WFLAG_RAIN)
	{
		HOBJECT hObj = m_Rain.GetObject();
		if (hObj)
		{
			bool bEnable = GetConsoleInt("EnableWeatherFX",1) != 0;
			if (bEnable)
			{
				m_Rain.Update();
			}
			g_pCommonLT->SetObjectFlags(hObj, OFT_Flags, bEnable ? FLAG_VISIBLE : 0, FLAG_VISIBLE);
		}

		// Get the camera's position...Make rain systems far away from the
		// camera more transparent...

		HOBJECT hCamera = g_pPlayerMgr->GetCamera();
        if (!hCamera) return LTFALSE;

        LTVector vCamPos, vDist, vPos;
		g_pLTClient->GetObjectPos(m_Rain.GetObject(), &vPos);
		g_pLTClient->GetObjectPos(hCamera, &vCamPos);
		vCamPos.y = vPos.y;  // Only wory about X and Z
		vDist = vCamPos - vPos;

        LTFLOAT fDistSqr         = vDist.MagSqr();
        LTFLOAT fFullAlphaSqr    = (RAIN_FULL_ALPHA_SCALE_DIST*RAIN_FULL_ALPHA_SCALE_DIST);
        LTFLOAT fMinAlphaSqr     = (RAIN_MIN_ALPHA_SCALE_DIST*RAIN_MIN_ALPHA_SCALE_DIST);

        LTFLOAT r, g, b, a;
		m_pClientDE->GetObjectColor(m_Rain.GetObject(), &r, &g, &b, &a);

		if (fDistSqr <= fFullAlphaSqr)
		{
			a = RAIN_MAX_ALPHA;  // Full alpha
		}
		else  // Calculate new alpha
		{
            LTFLOAT fDistOffset  = fDistSqr - fFullAlphaSqr;
            LTFLOAT fTotalDist   = fMinAlphaSqr - fFullAlphaSqr;

			a = RAIN_MAX_ALPHA - ((RAIN_MAX_ALPHA - RAIN_MIN_ALPHA) * (fDistOffset / fTotalDist));
			a = a < RAIN_MIN_ALPHA ? RAIN_MIN_ALPHA : a;
		}

		m_pClientDE->SetObjectColor(m_Rain.GetObject(), r, g, b, a);

		// Update Splash fx...

        hObj = LTNULL;

		for (int i=0; i < NUM_SPLASH_SPRITES; i++)
		{
			hObj = m_Splash[i].GetObject();

			if (hObj)
			{
                uint32 dwFlags;
				g_pCommonLT->GetObjectFlags(hObj, OFT_Flags, dwFlags);

				if (dwFlags & FLAG_VISIBLE)
				{
					if (!m_Splash[i].Update())
					{
						// If the sprite is done, hide it...

						g_pCommonLT->SetObjectFlags(hObj, OFT_Flags, 0, FLAG_VISIBLE);
					}
				}
			}
		}
	}

    return LTTRUE;
}
void CWeaponFX::CreateBloodSplatFX()
{
	CGameSettings* pSettings = g_pInterfaceMgr->GetSettings();
	if (!pSettings || !pSettings->Gore()) return;

	CSFXMgr* psfxMgr = g_pGameClientShell->GetSFXMgr();
	if (!psfxMgr) return;

	CSpecialFX* pFX = LTNULL;

	LTFLOAT fRange = g_vtBloodSplatsRange.GetFloat();

	// See if we should make some blood splats...

	ClientIntersectQuery iQuery;
	IntersectInfo iInfo;

	iQuery.m_From = m_vPos;

	LTVector vDir = m_vDir;

	// Create some blood splats...

	int nNumSplats = GetRandom((int)g_vtBloodSplatsMinNum.GetFloat(), (int)g_vtBloodSplatsMaxNum.GetFloat());

	LTVector vU, vR, vF;
    g_pLTClient->GetRotationVectors(&m_rDirRot, &vU, &vR, &vF);

	for (int i=0; i < nNumSplats; i++)
	{
		LTVector vDir = m_vDir;

		// Perturb direction after first splat...

		if (i > 0)
		{
			float fPerturb = g_vtBloodSplatsPerturb.GetFloat();

			float fRPerturb = (GetRandom(-fPerturb, fPerturb))/1000.0f;
			float fUPerturb = (GetRandom(-fPerturb, fPerturb))/1000.0f;

			vDir += (vR * fRPerturb);
			vDir += (vU * fUPerturb);
		}

		iQuery.m_To = vDir * fRange;
		iQuery.m_To += m_vPos;
		iQuery.m_Flags = IGNORE_NONSOLID | INTERSECT_HPOLY;

		if (g_pLTClient->IntersectSegment(&iQuery, &iInfo) && IsMainWorld(iInfo.m_hObject))
		{
			SurfaceType eType = GetSurfaceType(iInfo);
			if (eType == ST_SKY || eType == ST_INVISIBLE)
			{
				return; // Don't leave blood on the sky
			}


			LTBOOL bBigBlood = (LTBOOL)GetConsoleInt("BigBlood", 0);

			// Create a blood splat...

			BSCREATESTRUCT sc;

			g_pLTClient->AlignRotation(&(sc.rRot), &(iInfo.m_Plane.m_Normal), LTNULL);

			// Randomly rotate the blood splat

			g_pLTClient->RotateAroundAxis(&(sc.rRot), &(iInfo.m_Plane.m_Normal), GetRandom(0.0f, MATH_CIRCLE));

			LTVector vTemp = vDir * -2.0f;
			sc.vPos = iInfo.m_Point + vTemp;  // Off the wall a bit
			sc.vVel.Init(0.0f, 0.0f, 0.0f);

			sc.vInitialScale.Init(1.0f, 1.0f, 1.0f);
			sc.vInitialScale.x	= GetRandom(g_vtBloodSplatsMinScale.GetFloat(), g_vtBloodSplatsMaxScale.GetFloat());

			if (bBigBlood) sc.vInitialScale.x *= g_vtBigBloodSizeScale.GetFloat();

			sc.vInitialScale.y	= sc.vInitialScale.x;
			sc.vFinalScale		= sc.vInitialScale;

			sc.dwFlags			= FLAG_VISIBLE | FLAG_ROTATEABLESPRITE | FLAG_NOLIGHT;
			sc.fLifeTime		= GetRandom(g_vtBloodSplatsMinLifetime.GetFloat(), g_vtBloodSplatsMaxLifetime.GetFloat());

			if (bBigBlood) sc.fLifeTime *= g_vtBigBloodLifeScale.GetFloat();

			sc.fInitialAlpha	= 1.0f;
			sc.fFinalAlpha		= 0.0f;
			sc.nType			= OT_SPRITE;
			sc.bMultiply		= LTTRUE;

			char* pBloodFiles[] =
			{
				"Sfx\\Test\\Spr\\BloodL1.spr",
				"Sfx\\Test\\Spr\\BloodL2.spr",
				"Sfx\\Test\\Spr\\BloodL3.spr",
				"Sfx\\Test\\Spr\\BloodL4.spr"
			};

			sc.pFilename = pBloodFiles[GetRandom(0,3)];

			pFX = psfxMgr->CreateSFX(SFX_SCALE_ID, &sc);
			if (pFX) pFX->Update();
		}
		else if (i==0)
		{
			// Didn't hit anything straight back, do don't bother to
			// do anymore...

			return;
		}
	}
}
Example #3
0
void CDestructibleModel::SetSurfaceType()
{
	uint32 dwSurfUsrFlgs = 0;
	SurfaceType eSurfType = ST_UNKNOWN;

	// See if this object is a world model...

	if (GetObjectType(m_hObject) == OT_WORLDMODEL)
	{
        LTBOOL bDoBruteForce = LTTRUE;

		// See if we have a surface override...

		if (m_hstrSurfaceOverride)
		{
			const char* pSurfName = g_pLTServer->GetStringData(m_hstrSurfaceOverride);
			if (pSurfName)
			{
				SURFACE* pSurf = g_pSurfaceMgr->GetSurface(pSurfName);
				if (pSurf && pSurf->eType != ST_UNKNOWN)
				{
					eSurfType = pSurf->eType;
					bDoBruteForce = LTFALSE;
				}
			}
		}


		// Determine our surface...the hard way...

		if (bDoBruteForce)
		{
			IntersectQuery qInfo;
			IntersectInfo iInfo;

			LTVector vPos, vDims;
			LTRotation rRot;

			g_pLTServer->GetObjectPos(m_hObject, &vPos);
			g_pLTServer->GetObjectRotation(m_hObject, &rRot);
			g_pPhysicsLT->GetObjectDims(m_hObject, &vDims);

			LTFLOAT fMaxDims = vDims.x;
			fMaxDims = Max(fMaxDims, vDims.y);
			fMaxDims = Max(fMaxDims, vDims.z);

			qInfo.m_From = vPos + (rRot.Forward() * (fMaxDims + 1));
			qInfo.m_To   = vPos;

			qInfo.m_Flags = INTERSECT_OBJECTS | IGNORE_NONSOLID | INTERSECT_HPOLY;

			SurfaceType eType = ST_UNKNOWN;

			if (g_pLTServer->IntersectSegment(&qInfo, &iInfo))
			{
				if (iInfo.m_hObject == m_hObject)
				{
					eSurfType = GetSurfaceType(iInfo);
				}
			}
		}
	}
	else
	{
		DEBRIS* pDebris = g_pDebrisMgr->GetDebris(m_nDebrisId);
		if (pDebris)
		{
			eSurfType = pDebris->eSurfaceType;
		}
	}

	dwSurfUsrFlgs = SurfaceToUserFlag(eSurfType);
	g_pCommonLT->SetObjectFlags(m_hObject, OFT_User, dwSurfUsrFlgs, dwSurfUsrFlgs);

	g_pCommonLT->GetObjectFlags(m_hObject, OFT_Flags, m_dwOriginalFlags);
}
Example #4
0
LTBOOL CShellCasingFX::Update()
{
    if (!m_hObject || !m_pClientDE) return LTFALSE;

	if (g_pGameClientShell->IsServerPaused())
	{
		return LTTRUE;
	}

	m_fElapsedTime += g_pGameClientShell->GetFrameTime();
	m_fDieTime -= g_pGameClientShell->GetFrameTime();
	
    if (m_fDieTime <= 0.0f) return LTFALSE;

	// Update object scale if necessary...

	LTVector vScale;
	m_pClientDE->GetObjectScale(m_hObject, &vScale);

	if (vScale != m_vFinalScale)
	{
		if (m_fElapsedTime <= g_vtShellScaleTime.GetFloat())
		{
			LTVector vScaleRange = (m_vFinalScale - m_vInitialScale);

			vScale = m_vInitialScale + (vScaleRange * (m_fElapsedTime/g_vtShellScaleTime.GetFloat()));

			if (vScale > m_vFinalScale)
			{
				vScale = m_vFinalScale;
			}

			m_pClientDE->SetObjectScale(m_hObject, &vScale);
		}
		else
		{
			m_pClientDE->SetObjectScale(m_hObject, &m_vFinalScale);
		}
	}

    if (m_bResting) return LTTRUE;

    LTRotation rRot;
	g_pLTClient->GetObjectRotation(m_hObject, &rRot);

	// If velocity slows enough, and we're on the ground, just stop bouncing and just wait to expire.

	if (m_movingObj.m_dwPhysicsFlags & MO_RESTING)
	{
        m_bResting = LTTRUE;

		// Stop the spinning...

		rRot.Rotate(rRot.Up(), m_fYaw);
		g_pLTClient->SetObjectRotation(m_hObject, &rRot);

		// Shell is at rest, we can add a check here to see if we really want
		// to keep it around depending on detail settings...

		//HLOCALOBJ hObjs[1];
        //uint32 nNumFound, nBogus;
		//m_pClientDE->FindObjectsInSphere(&m_movingObj.m_vPos, 64.0f, hObjs, 1, &nBogus, &nNumFound);

		// Remove thyself...
        //if (nNumFound > 15) return LTFALSE;
	}
	else
	{
		if (m_fPitchVel != 0 || m_fYawVel != 0)
		{
            LTFLOAT fDeltaTime = g_pGameClientShell->GetFrameTime();

			m_fPitch += m_fPitchVel * fDeltaTime;
			m_fYaw   += m_fYawVel * fDeltaTime;

			rRot.Rotate(rRot.Up(), m_fYaw);
			rRot.Rotate(rRot.Right(), m_fPitch);
			g_pLTClient->SetObjectRotation(m_hObject, &rRot);
		}
	}


    LTVector vNewPos;
    if (UpdateMovingObject(LTNULL, &m_movingObj, vNewPos))
	{
		ClientIntersectInfo info;
		LTBOOL bBouncedOnGround = LTFALSE;
        if (BounceMovingObject(LTNULL, &m_movingObj, vNewPos, &info, 
			INTERSECT_HPOLY, true, bBouncedOnGround))
		{
			// If we hit the sky/invisible surface we're done...

			SurfaceType eType = GetSurfaceType(info);
			if (eType == ST_SKY || eType == ST_INVISIBLE)
			{
                return LTFALSE;
			}

			if (m_nBounceCount >= MAX_BOUNCE_COUNT)
			{
				if (!(m_movingObj.m_dwPhysicsFlags & MO_LIQUID))
				{
					SURFACE* pSurf = g_pSurfaceMgr->GetSurface(eType);
					if (pSurf)
					{
						// Play appropriate sound...

						if (pSurf->szShellImpactSnds[0])
						{
							g_pClientSoundMgr->PlaySoundFromPos(vNewPos, pSurf->szShellImpactSnds[0], pSurf->fShellSndRadius,
								SOUNDPRIORITY_MISC_LOW);
						}
					}
				}
			}

			// Adjust the bouncing..

			m_fPitchVel *= 0.75f;
			m_fYawVel	*= -0.75f;

			m_nBounceCount--;

			if (m_nBounceCount <= 0)
			{
				m_movingObj.m_dwPhysicsFlags |= MO_RESTING;
			}
		}

		m_movingObj.m_vPos = vNewPos;

        if (g_pCommonLT->GetPointStatus(&vNewPos) == LT_OUTSIDE)
		{
            return LTFALSE;
		}

		g_pLTClient->SetObjectPos(m_hObject, &vNewPos);
	}

    return LTTRUE;
}
Example #5
0
void CTargetMgr::CheckForIntersect(float &fDistAway)
{
	m_hTarget = LTNULL;
	m_ActivationData.Init();

	uint32 dwUsrFlags = 0;
	const float fMaxDist = 100000.0f;

	// Cast ray from the camera to see if there is an object to activate...

    LTRotation rRot;
    LTVector vPos;

	HLOCALOBJ hCamera = g_pPlayerMgr->GetCamera();
	g_pLTClient->GetObjectPos(hCamera, &vPos);
	g_pLTClient->GetObjectRotation(hCamera, &rRot);

 	m_ActivationData.m_vPos = vPos;
	m_ActivationData.m_rRot = rRot;

	IntersectQuery IQuery;
	IntersectInfo IInfo;

	IQuery.m_From = vPos;
	IQuery.m_To   = IQuery.m_From + (rRot.Forward() * fMaxDist);

	// NOTE the use of the CHECK_FROM_POINT_INSIDE_OBJECTS flag.  This flag will
	// make sure that any objects that m_From is inside are considered
	IQuery.m_Flags = CHECK_FROM_POINT_INSIDE_OBJECTS | INTERSECT_HPOLY | INTERSECT_OBJECTS | IGNORE_NONSOLID;

	IQuery.m_FilterActualIntersectFn	= ActivateFilterFn;
	IQuery.m_pActualIntersectUserData	= (void*)&IQuery;
	IQuery.m_PolyFilterFn				= DoVectorPolyFilterFn;

	// [KLS 8/3/02] - ActivateFilterFn may save an object to use that may not be
	// the best activation choice (i.e., a fallback choice).  However, if a
	// better choice isn't found, the fallback choice should be used.  That
	// fallback choice is stored in g_adFallbackActivationObject so we clear
	// it here...
	g_adFallbackActivationObject.Init();

	if (g_pLTClient->IntersectSegment(&IQuery, &IInfo))
	{
		m_ActivationData.m_vIntersect = IInfo.m_Point;

        if (IsMainWorld(IInfo.m_hObject))
		{
			if (IInfo.m_hPoly != INVALID_HPOLY)
			{
				SurfaceType eType = GetSurfaceType(IInfo.m_hPoly);
				SURFACE *pSurf = g_pSurfaceMgr->GetSurface(eType);

				// See if the surface we tried to activate has an activation
				// sound...If so, the user can activate it...

				if (pSurf && pSurf->szActivationSnd[0] && pSurf->fActivationSndRadius > 0)
				{
					m_hTarget = IInfo.m_hObject;
					m_ActivationData.m_hTarget = m_hTarget;
					m_ActivationData.m_nSurfaceType = eType;
				}
			}
		}
		else
		{
			LTVector vObjPos = m_ActivationData.m_vIntersect;
			vObjPos -= vPos;

			if (vObjPos.Mag() <= fMaxDist)
			{
				m_hTarget = IInfo.m_hObject;
				m_ActivationData.m_hTarget = m_hTarget;
			}
		}

		// Calculate how far away the object is...

		LTVector vDist = m_ActivationData.m_vIntersect - vPos;
		fDistAway = vDist.Mag();
	}
	
	// [KLS 8/3/02] - Use the fallback object if we have one and we didn't 
	// find another object more suitable object... 

	bool bCanUseFallback = (m_ActivationData.m_hTarget ? false : true);
	if (!bCanUseFallback)
	{
		// We can still use the fallback object if it isn't the world or a
		// world model...

		if (IsMainWorld(m_ActivationData.m_hTarget) || 
			OT_WORLDMODEL == GetObjectType(m_ActivationData.m_hTarget))
		{
			bCanUseFallback = true;
		}
	}

	if ( bCanUseFallback && g_adFallbackActivationObject.m_hTarget )
	{
		// Ok we hit the fallback object reset some of our target data
	
		LTVector vObjPos;
		g_pLTClient->GetObjectPos(g_adFallbackActivationObject.m_hTarget, &vObjPos);

		m_ActivationData.m_vIntersect = vObjPos;

		vObjPos -= vPos;

		if (vObjPos.Mag() <= fMaxDist)
		{
			m_hTarget = g_adFallbackActivationObject.m_hTarget;
			m_ActivationData.m_hTarget = m_hTarget;
		}

		// Calculate how far away the object is...

		LTVector vDist = m_ActivationData.m_vIntersect - vPos;
		fDistAway = vDist.Mag();
	}
}
void CProjectileFX::Detonate(CollisionInfo* pInfo)
{
	if (!m_pClientDE || m_bDetonated) return;

    m_bDetonated = LTTRUE;

	SurfaceType eType = ST_UNKNOWN;

    LTVector vPos;
	m_pClientDE->GetObjectPos(m_hServerObject, &vPos);

	// Determine the normal of the surface we are impacting on...

    LTVector vNormal;
	VEC_SET(vNormal, 0.0f, 1.0f, 0.0f);

	if (pInfo)
	{
		if (pInfo->m_hObject)
		{
			eType = GetSurfaceType(pInfo->m_hObject);
		}
		else if (pInfo->m_hPoly)
		{
			eType = GetSurfaceType(pInfo->m_hPoly);

			VEC_COPY(vNormal, pInfo->m_Plane.m_Normal);

            LTRotation rRot;
            m_pClientDE->AlignRotation(&rRot, &vNormal, LTNULL);
			m_pClientDE->SetObjectRotation(m_hServerObject, &rRot);

			// Calculate where we really hit the plane...

            LTVector vVel, vP0, vP1;
			m_pClientDE->Physics()->GetVelocity(m_hServerObject, &vVel);

			VEC_COPY(vP1, vPos);
			VEC_MULSCALAR(vVel, vVel, g_pGameClientShell->GetFrameTime());
			VEC_SUB(vP0, vP1, vVel);

            LTFLOAT fDot1 = VEC_DOT(pInfo->m_Plane.m_Normal, vP0) - pInfo->m_Plane.m_Dist;
            LTFLOAT fDot2 = VEC_DOT(pInfo->m_Plane.m_Normal, vP1) - pInfo->m_Plane.m_Dist;

			if (fDot1 < 0.0f && fDot2 < 0.0f || fDot1 > 0.0f && fDot2 > 0.0f)
			{
				VEC_COPY(vPos, vP1);
			}
			else
			{
                LTFLOAT fPercent = -fDot1 / (fDot2 - fDot1);
				VEC_LERP(vPos, vP0, vP1, fPercent);
			}
		}
	}
	else
	{
		// Since pInfo was null, this means the projectile's lifetime was up,
		// so we just blow-up in the air.

		eType = ST_AIR;
	}


    HOBJECT hObj = !!pInfo ? pInfo->m_hObject : LTNULL;
	::AddLocalImpactFX(hObj, m_vFirePos, vPos, vNormal, eType, m_vPath,
					   m_nWeaponId, m_nAmmoId, 0);

    m_bWantRemove = LTTRUE;
}
Example #7
0
void CTargetMgr::CheckForIntersect(float &fDistAway)
{
	m_hTarget = NULL;
	m_ActivationData.Init();


	// Cast ray from the camera to see if there is an object to activate...

	LTRotation const& rRot = g_pPlayerMgr->GetPlayerCamera()->GetCameraRotation( );;
	LTVector const& vPos = g_pPlayerMgr->GetPlayerCamera()->GetCameraPos( );

 	m_ActivationData.m_vPos = vPos;
	m_ActivationData.m_rRot = rRot;

	IntersectQuery IQuery;
	IntersectInfo IInfo;

	IQuery.m_From = vPos;
	IQuery.m_To   = IQuery.m_From + (rRot.Forward() * kMaxDistance);

	// NOTE the use of the CHECK_FROM_POINT_INSIDE_OBJECTS flag.  This flag will
	// make sure that any objects that m_From is inside are considered
	IQuery.m_Flags = CHECK_FROM_POINT_INSIDE_OBJECTS | INTERSECT_HPOLY | INTERSECT_OBJECTS | IGNORE_NONSOLID;

	IQuery.m_FilterActualIntersectFn	= ActivateFilterFn;
	IQuery.m_pActualIntersectUserData	= (void*)&IQuery;
	IQuery.m_PolyFilterFn				= NULL;

	// [KLS 8/3/02] - ActivateFilterFn may save an object to use that may not be
	// the best activation choice (i.e., a fallback choice).  However, if a
	// better choice isn't found, the fallback choice should be used.  That
	// fallback choice is stored in g_adFallbackActivationObject so we clear
	// it here...
	g_adFallbackActivationObject.Init();

	if (g_pLTClient->IntersectSegment(IQuery, &IInfo))
	{
		m_ActivationData.m_vIntersect = IInfo.m_Point;

		bool bHitSky = false;

        if (IsMainWorld(IInfo.m_hObject))
		{
			if (IInfo.m_hPoly != INVALID_HPOLY)
			{
				SurfaceType eType = GetSurfaceType(IInfo.m_hPoly);
				HSURFACE hSurf = g_pSurfaceDB->GetSurface(eType);

				// See if the surface we tried to activate has an activation
				// sound...If so, the user can activate it...

				if (hSurf)
				{
					HRECORD hActSnd = g_pSurfaceDB->GetRecordLink(hSurf,SrfDB_Srf_rActivationSnd);
					if (hActSnd && g_pSoundDB->GetFloat(hActSnd,SndDB_fOuterRadius) > 0) 
					{
						m_hTarget = IInfo.m_hObject;
						m_ActivationData.m_hTarget = m_hTarget;
						m_ActivationData.m_nSurfaceType = eType;
						m_ActivationData.m_hActivateSnd = hActSnd;
					}

					bHitSky = (ST_SKY == eType);
				}
			}
		}
		else
		{
			LTVector vObjPos = m_ActivationData.m_vIntersect;
			vObjPos -= vPos;

			if (vObjPos.Mag() <= kMaxDistance)
			{
				m_hTarget = IInfo.m_hObject;
				m_ActivationData.m_hTarget = m_hTarget;
			}
		}

		// Calculate how far away the object is...

		LTVector vDist = m_ActivationData.m_vIntersect - vPos;
		if (bHitSky)
			fDistAway = kMaxDistance;
		else
			fDistAway = vDist.Mag();
	}
	
	// [KLS 8/3/02] - Use the fallback object if we have one and we didn't 
	// find another object more suitable object... 

	bool bCanUseFallback = (m_ActivationData.m_hTarget ? false : true);
	if (!bCanUseFallback)
	{
		// We can still use the fallback object if it isn't the world or a
		// world model...

		if (IsMainWorld(m_ActivationData.m_hTarget) || 
			OT_WORLDMODEL == GetObjectType(m_ActivationData.m_hTarget))
		{
			bCanUseFallback = true;
		}
	}

	if ( bCanUseFallback && g_adFallbackActivationObject.m_hTarget )
	{
		// Ok we hit the fallback object reset some of our target data
	
		LTVector vObjPos;
		g_pLTClient->GetObjectPos(g_adFallbackActivationObject.m_hTarget, &vObjPos);

		m_ActivationData.m_vIntersect = vObjPos;

		vObjPos -= vPos;

		if (vObjPos.Mag() <= kMaxDistance)
		{
			m_hTarget = g_adFallbackActivationObject.m_hTarget;
			m_ActivationData.m_hTarget = m_hTarget;
		}

		// Calculate how far away the object is...

		LTVector vDist = m_ActivationData.m_vIntersect - vPos;
		fDistAway = vDist.Mag();
	}
}
void
subbrep_planar_init(struct subbrep_object_data *data)
{
    if (!data) return;
    if (data->planar_obj) return;
    BU_GET(data->planar_obj, struct subbrep_object_data);
    subbrep_object_init(data->planar_obj, data->brep);
    bu_vls_sprintf(data->planar_obj->key, "%s", bu_vls_addr(data->key));
    data->planar_obj->obj_cnt = data->obj_cnt;
    (*data->obj_cnt)++;
    bu_vls_sprintf(data->planar_obj->name_root, "%s_%d", bu_vls_addr(data->name_root), *(data->obj_cnt));
    data->planar_obj->type = PLANAR_VOLUME;

    data->planar_obj->local_brep = ON_Brep::New();
    std::map<int, int> face_map;
    std::map<int, int> surface_map;
    std::map<int, int> edge_map;
    std::map<int, int> vertex_map;
    std::map<int, int> loop_map;
    std::map<int, int> c3_map;
    std::map<int, int> c2_map;
    std::map<int, int> trim_map;
    std::set<int> faces;
    std::set<int> fil;
    std::set<int> loops;
    std::set<int> skip_verts;
    std::set<int> skip_edges;
    std::set<int> keep_verts;
    std::set<int> partial_edges;
    std::set<int> isolated_trims;  // collect 2D trims whose parent loops aren't fully included here
    array_to_set(&faces, data->faces, data->faces_cnt);
    array_to_set(&fil, data->fil, data->fil_cnt);
    array_to_set(&loops, data->loops, data->loops_cnt);
    std::map<int, std::set<int> > face_loops;
    std::map<int, std::set<int> >::iterator fl_it;
    std::set<int>::iterator l_it;

    for (int i = 0; i < data->edges_cnt; i++) {
	int c3i;
	int new_edge_curve = 0;
	const ON_BrepEdge *old_edge = &(data->brep->m_E[data->edges[i]]);
	//std::cout << "old edge: " << old_edge->Vertex(0)->m_vertex_index << "," << old_edge->Vertex(1)->m_vertex_index << "\n";

	// See if the vertices from this edge play a role in the planar volume
	int use_edge = 2;
	for (int vi = 0; vi < 2; vi++) {
	    int vert_test = -1;
	    int vert_ind = old_edge->Vertex(vi)->m_vertex_index;
	    if (skip_verts.find(vert_ind) != skip_verts.end()) {
		vert_test = 1;
	    }
	    if (vert_test == -1 && keep_verts.find(vert_ind) != keep_verts.end()) {
		vert_test = 0;
	    }
	    if (vert_test == -1) {
		vert_test = characterize_vert(data, old_edge->Vertex(vi));
		if (vert_test) {
		    skip_verts.insert(vert_ind);
		    ON_3dPoint vp = old_edge->Vertex(vi)->Point();
		    bu_log("vert %d (%f %f %f): %d\n", vert_ind, vp.x, vp.y, vp.z, vert_test);
		} else {
		    keep_verts.insert(vert_ind);
		}
	    }
	    if (vert_test == 1) {
		use_edge--;
	    }
	}

	if (use_edge == 0) {
	    bu_log("skipping edge %d - both verts are skips\n", old_edge->m_edge_index);
	    skip_edges.insert(old_edge->m_edge_index);
	    continue;
	}

	if (use_edge == 1) {
	    bu_log("One of the verts for edge %d is a skip.\n", old_edge->m_edge_index);
	    partial_edges.insert(old_edge->m_edge_index);
	    continue;
	}

	// Get the 3D curves from the edges
	if (c3_map.find(old_edge->EdgeCurveIndexOf()) == c3_map.end()) {
	    ON_Curve *nc = old_edge->EdgeCurveOf()->Duplicate();
	    ON_Curve *tc = old_edge->EdgeCurveOf()->Duplicate();
	    if (tc->IsLinear()) {
		c3i = data->planar_obj->local_brep->AddEdgeCurve(nc);
		c3_map[old_edge->EdgeCurveIndexOf()] = c3i;
	    } else {
		ON_Curve *c3 = new ON_LineCurve(old_edge->Vertex(0)->Point(), old_edge->Vertex(1)->Point());
		c3i = data->planar_obj->local_brep->AddEdgeCurve(c3);
		c3_map[old_edge->EdgeCurveIndexOf()] = c3i;
		new_edge_curve = 1;
	    }
	} else {
	    c3i = c3_map[old_edge->EdgeCurveIndexOf()];
	}


	// Get the vertices from the edges
	int v[2];
	for (int vi = 0; vi < 2; vi++) {
	    if (vertex_map.find(old_edge->Vertex(vi)->m_vertex_index) == vertex_map.end()) {
		ON_BrepVertex& newvvi = data->planar_obj->local_brep->NewVertex(old_edge->Vertex(vi)->Point(), old_edge->Vertex(vi)->m_tolerance);
		v[vi] = newvvi.m_vertex_index;
		vertex_map[old_edge->Vertex(vi)->m_vertex_index] = v[vi];
	    } else {
		v[vi] = vertex_map[old_edge->Vertex(vi)->m_vertex_index];
	    }
	}

	ON_BrepEdge& new_edge = data->planar_obj->local_brep->NewEdge(data->planar_obj->local_brep->m_V[v[0]], data->planar_obj->local_brep->m_V[v[1]], c3i, NULL ,0);
	edge_map[old_edge->m_edge_index] = new_edge.m_edge_index;

	// Get the 2D curves from the trims
	for (int j = 0; j < old_edge->TrimCount(); j++) {
	    ON_BrepTrim *old_trim = old_edge->Trim(j);
	    if (faces.find(old_trim->Face()->m_face_index) != faces.end()) {
		if (c2_map.find(old_trim->TrimCurveIndexOf()) == c2_map.end()) {
		    ON_Curve *nc = old_trim->TrimCurveOf()->Duplicate();
		    int c2i = data->planar_obj->local_brep->AddTrimCurve(nc);
		    c2_map[old_trim->TrimCurveIndexOf()] = c2i;
		    //std::cout << "c2i: " << c2i << "\n";
		}
	    }
	}

	// Get the faces and surfaces from the trims
	for (int j = 0; j < old_edge->TrimCount(); j++) {
	    ON_BrepTrim *old_trim = old_edge->Trim(j);
	    if (face_map.find(old_trim->Face()->m_face_index) == face_map.end()) {
		if (faces.find(old_trim->Face()->m_face_index) != faces.end()) {
		    ON_Surface *ns = old_trim->Face()->SurfaceOf()->Duplicate();
		    ON_Surface *ts = old_trim->Face()->SurfaceOf()->Duplicate();
		    if (ts->IsPlanar(NULL, BREP_PLANAR_TOL)) {
			int nsid = data->planar_obj->local_brep->AddSurface(ns);
			surface_map[old_trim->Face()->SurfaceIndexOf()] = nsid;
			ON_BrepFace &new_face = data->planar_obj->local_brep->NewFace(nsid);
			face_map[old_trim->Face()->m_face_index] = new_face.m_face_index;
			//std::cout << "old face " << old_trim->Face()->m_face_index << " is now " << new_face.m_face_index << "\n";
			if (fil.find(old_trim->Face()->m_face_index) != fil.end()) {
			    data->planar_obj->local_brep->FlipFace(new_face);
			}
		    }
		}
	    }
	}

	// Get the loops from the trims
	for (int j = 0; j < old_edge->TrimCount(); j++) {
	    ON_BrepTrim *old_trim = old_edge->Trim(j);
	    ON_BrepLoop *old_loop = old_trim->Loop();
	    if (face_map.find(old_trim->Face()->m_face_index) != face_map.end()) {
		if (loops.find(old_loop->m_loop_index) != loops.end()) {
		    if (loop_map.find(old_loop->m_loop_index) == loop_map.end()) {
			face_loops[old_trim->Face()->m_face_index].insert(old_loop->m_loop_index);
		    }
		}
	    }
	}
    }
    for (fl_it = face_loops.begin(); fl_it != face_loops.end(); fl_it++) {
	int loop_cnt = fl_it->second.size();
	if (loop_cnt == 1) {
	    // If we have only one loop on a face it's an outer loop,
	    // whatever it was in the original brep.
	    const ON_BrepLoop *old_loop = &(data->brep->m_L[*(fl_it->second.begin())]);
	    ON_BrepLoop &nl = data->planar_obj->local_brep->NewLoop(ON_BrepLoop::outer, data->planar_obj->local_brep->m_F[face_map[fl_it->first]]);
	    loop_map[old_loop->m_loop_index] = nl.m_loop_index;
	} else {
	    bu_log("loop_cnt: %d\n", loop_cnt);
	    // If we ended up with multiple loops, one of them should be an outer loop
	    // and the rest inner loops
	    // Get the outer loop first
	    for (l_it = fl_it->second.begin(); l_it != fl_it->second.end(); l_it++) {
		const ON_BrepLoop *old_loop = &(data->brep->m_L[*l_it]);
		if (data->brep->LoopDirection(data->brep->m_L[*l_it]) == 1) {
		    ON_BrepLoop &nl = data->planar_obj->local_brep->NewLoop(ON_BrepLoop::outer, data->planar_obj->local_brep->m_F[face_map[fl_it->first]]);
		    loop_map[old_loop->m_loop_index] = nl.m_loop_index;
		}
	    }
	    // Now get the inner loops;
	    for (l_it = fl_it->second.begin(); l_it != fl_it->second.end(); l_it++) {
		const ON_BrepLoop *old_loop = &(data->brep->m_L[*l_it]);
		if (data->brep->LoopDirection(data->brep->m_L[*l_it]) != 1) {
		    ON_BrepLoop &nl = data->planar_obj->local_brep->NewLoop(ON_BrepLoop::inner, data->planar_obj->local_brep->m_F[face_map[fl_it->first]]);
		    loop_map[old_loop->m_loop_index] = nl.m_loop_index;
		}
	    }
	}
    }

    // Now, create new trims using the old loop definitions and the maps
    std::map<int, int>::iterator loop_it;
    std::set<int> evaluated;
    for (loop_it = loop_map.begin(); loop_it != loop_map.end(); loop_it++) {
	const ON_BrepLoop *old_loop = &(data->brep->m_L[(*loop_it).first]);
	ON_BrepLoop &new_loop = data->planar_obj->local_brep->m_L[(*loop_it).second];
	for (int j = 0; j < old_loop->TrimCount(); j++) {
	    const ON_BrepTrim *old_trim = old_loop->Trim(j);
	    ON_BrepEdge *o_edge = old_trim->Edge();
	    if (!o_edge) {
		/* If we didn't have an edge originally, we need to add the 2d curve here */
		if (c2_map.find(old_trim->TrimCurveIndexOf()) == c2_map.end()) {
		    ON_Curve *nc = old_trim->TrimCurveOf()->Duplicate();
		    int c2i = data->planar_obj->local_brep->AddTrimCurve(nc);
		    c2_map[old_trim->TrimCurveIndexOf()] = c2i;
		}
		if (vertex_map.find(old_trim->Vertex(0)->m_vertex_index) == vertex_map.end()) {
		    ON_BrepVertex& newvs = data->planar_obj->local_brep->NewVertex(old_trim->Vertex(0)->Point(), old_trim->Vertex(0)->m_tolerance);
		    vertex_map[old_trim->Vertex(0)->m_vertex_index] = newvs.m_vertex_index;
		    ON_BrepTrim &nt = data->planar_obj->local_brep->NewSingularTrim(newvs, new_loop, old_trim->m_iso, c2_map[old_trim->TrimCurveIndexOf()]);
		    nt.m_tolerance[0] = old_trim->m_tolerance[0];
		    nt.m_tolerance[1] = old_trim->m_tolerance[1];
		} else {
		    ON_BrepTrim &nt = data->planar_obj->local_brep->NewSingularTrim(data->planar_obj->local_brep->m_V[vertex_map[old_trim->Vertex(0)->m_vertex_index]], new_loop, old_trim->m_iso, c2_map[old_trim->TrimCurveIndexOf()]);
		    nt.m_tolerance[0] = old_trim->m_tolerance[0];
		    nt.m_tolerance[1] = old_trim->m_tolerance[1];
		}
		continue;
	    }

	    if (evaluated.find(o_edge->m_edge_index) != evaluated.end()) {
		bu_log("edge %d already handled, continuing...\n", o_edge->m_edge_index);
		continue;
	    }

	    // Don't use a trim connected to an edge we are skipping
	    if (skip_edges.find(o_edge->m_edge_index) != skip_edges.end()) {
		bu_log("edge %d is skipped, continuing...\n", o_edge->m_edge_index);
		evaluated.insert(o_edge->m_edge_index);
		continue;
	    }

	    int is_partial = 0;
	    if (partial_edges.find(o_edge->m_edge_index) != partial_edges.end()) is_partial = 1;

	    if (!is_partial) {
		ON_BrepEdge &n_edge = data->planar_obj->local_brep->m_E[edge_map[o_edge->m_edge_index]];
		ON_Curve *ec = o_edge->EdgeCurveOf()->Duplicate();
		if (ec->IsLinear()) {
		    ON_BrepTrim &nt = data->planar_obj->local_brep->NewTrim(n_edge, old_trim->m_bRev3d, new_loop, c2_map[old_trim->TrimCurveIndexOf()]);
		    nt.m_tolerance[0] = old_trim->m_tolerance[0];
		    nt.m_tolerance[1] = old_trim->m_tolerance[1];
		    nt.m_iso = old_trim->m_iso;
		} else {
		    // Wasn't linear, but wasn't partial either - replace with a line
		    ON_Curve *c2_orig = old_trim->TrimCurveOf()->Duplicate();
		    ON_3dPoint p1 = c2_orig->PointAt(c2_orig->Domain().Min());
		    ON_3dPoint p2 = c2_orig->PointAt(c2_orig->Domain().Max());
		    ON_Curve *c2 = new ON_LineCurve(p1, p2);
		    c2->ChangeDimension(2);
		    int c2i = data->planar_obj->local_brep->AddTrimCurve(c2);
		    ON_BrepTrim &nt = data->planar_obj->local_brep->NewTrim(n_edge, old_trim->m_bRev3d, new_loop, c2i);
		    nt.m_tolerance[0] = old_trim->m_tolerance[0];
		    nt.m_tolerance[1] = old_trim->m_tolerance[1];
		    nt.m_iso = old_trim->m_iso;
		    delete c2_orig;
		}
		delete ec;
	    } else {
		// Partial edge - let the fun begin
		ON_3dPoint p1, p2;
		ON_BrepEdge *next_edge;
		bu_log("working a partial edge: %d\n", o_edge->m_edge_index);
		int v[2];
		v[0] = o_edge->Vertex(0)->m_vertex_index;
		v[1] = o_edge->Vertex(1)->m_vertex_index;
		// figure out which trim point we can use, the min or max
		int pos1 = 0;
		if (skip_verts.find(v[0]) != skip_verts.end()) {
		    pos1 = 1;
		}
		int j_next = j;
		ON_Curve *c2_orig = old_trim->TrimCurveOf()->Duplicate();
		ON_Curve *c2_next = NULL;
		int walk_dir = 1;
		// bump the loop iterator to get passed any skipped edges to
		// the next partial
		while (!c2_next) {
		    (walk_dir == 1) ? j_next++ : j_next--;
		    if (j_next == old_loop->TrimCount()) {
			j_next = 0;
		    }
		    if (j_next == -1) {
			j_next = old_loop->TrimCount() - 1;
		    }
		    const ON_BrepTrim *next_trim = old_loop->Trim(j_next);
		    next_edge = next_trim->Edge();
		    if (!next_edge) continue;
		    if (skip_edges.find(next_edge->m_edge_index) == skip_edges.end()) {
			if (partial_edges.find(next_edge->m_edge_index) != partial_edges.end()) {
			    bu_log("found next partial edge %d\n", next_edge->m_edge_index);
			    evaluated.insert(next_edge->m_edge_index);
			    c2_next = next_trim->TrimCurveOf()->Duplicate();
			} else {
			    bu_log("partial edge %d followed by non-partial %d, need to go the other way\n", o_edge->m_edge_index, next_edge->m_edge_index);
			    j_next--;
			    walk_dir = -1;
			}
		    } else {
			bu_log("skipping fully ignored edge %d\n", next_edge->m_edge_index);
			evaluated.insert(next_edge->m_edge_index);
		    }
		}
		int v2[2];
		v2[0] = next_edge->Vertex(0)->m_vertex_index;
		v2[1] = next_edge->Vertex(1)->m_vertex_index;
		// figure out which trim point we can use, the min or max
		int pos2 = 0;
		if (skip_verts.find(v2[0]) != skip_verts.end()) {
		    pos2 = 1;
		}

		int vmapped[2];
		if (vertex_map.find(o_edge->Vertex(pos1)->m_vertex_index) == vertex_map.end()) {
		    ON_BrepVertex& newvvi = data->planar_obj->local_brep->NewVertex(o_edge->Vertex(pos1)->Point(), o_edge->Vertex(pos1)->m_tolerance);
		    vertex_map[o_edge->Vertex(pos1)->m_vertex_index] = newvvi.m_vertex_index;
		}
		if (vertex_map.find(next_edge->Vertex(pos2)->m_vertex_index) == vertex_map.end()) {
		    ON_BrepVertex& newvvi = data->planar_obj->local_brep->NewVertex(next_edge->Vertex(pos2)->Point(), next_edge->Vertex(pos2)->m_tolerance);
		    vertex_map[next_edge->Vertex(pos2)->m_vertex_index] = newvvi.m_vertex_index;
		}

		// If walk_dir is -1, need to flip things around (I think...) the verts and trim points
		// will be swapped compared to a forward walk
		if (walk_dir == -1) {
		    vmapped[1] = vertex_map[o_edge->Vertex(pos1)->m_vertex_index];
		    vmapped[0] = vertex_map[next_edge->Vertex(pos2)->m_vertex_index];
		} else {
		    vmapped[0] = vertex_map[o_edge->Vertex(pos1)->m_vertex_index];
		    vmapped[1] = vertex_map[next_edge->Vertex(pos2)->m_vertex_index];
		}

		// New Edge curve
		ON_Curve *c3 = new ON_LineCurve(o_edge->Vertex(pos1)->Point(), next_edge->Vertex(pos2)->Point());
		int c3i = data->planar_obj->local_brep->AddEdgeCurve(c3);
		ON_BrepEdge& new_edge = data->planar_obj->local_brep->NewEdge(data->planar_obj->local_brep->m_V[vmapped[0]], data->planar_obj->local_brep->m_V[vmapped[1]], c3i, NULL ,0);

		// Again, flip if walk_dir is -1
		if (walk_dir == -1) {
		    p2 = c2_orig->PointAt(c2_orig->Domain().Min());
		    p1 = c2_next->PointAt(c2_orig->Domain().Max());
		} else {
		    p1 = c2_orig->PointAt(c2_orig->Domain().Min());
		    p2 = c2_next->PointAt(c2_orig->Domain().Max());
		}
		std::cout << "p1: " << pout(p1) << "\n";
		std::cout << "p2: " << pout(p2) << "\n";
		ON_Curve *c2 = new ON_LineCurve(p1, p2);
		c2->ChangeDimension(2);
		int c2i = data->planar_obj->local_brep->AddTrimCurve(c2);
		ON_BrepTrim &nt = data->planar_obj->local_brep->NewTrim(new_edge, false, new_loop, c2i);
		nt.m_tolerance[0] = old_trim->m_tolerance[0];
		nt.m_tolerance[1] = old_trim->m_tolerance[1];
		nt.m_iso = old_trim->m_iso;
		delete c2_orig;
		delete c2_next;
	    }
	}
    }

    // If there is a possibility of a negative volume for the planar solid, do a test.
    // The only way to get a negative planar solid in this context is if that solid is
    // "inside" a non-planar shape (it would be "part of" the parent shape if it were
    // planar and it would be a separate shape altogether if it were not topologically
    // connected.  So we take one partial edge, find its associated non-planar faces,
    // and collect all the partial and skipped edges from that face and any non-planar
    // faces associated with the other partial/skipped edges.
    //
    // TODO - We still have an unhandled possibility here - the self-intersecting
    // planar_obj.  For example:
    //
    //           *                *
    //       *       *        *       *
    //     *     *      *   *    *      *
    //    *     * *      * *    * *      *
    //   *     *   *           *   *      *
    //   * *  *     * * * * * *     *  *  *
    //
    if (partial_edges.size() > 0) {
	std::queue<int> connected_faces;
	std::set<int> relevant_edges;
	std::set<int>::iterator re_it;
	std::set<int> efaces;
	std::set<int>::iterator f_it;
	std::set<int> found_faces;
	const ON_BrepEdge *seed_edge = &(data->brep->m_E[*partial_edges.begin()]);
	for (int j = 0; j < seed_edge->TrimCount(); j++) {
	    ON_BrepTrim *trim = seed_edge->Trim(j);
	    efaces.insert(trim->Face()->m_face_index);
	}
	for(f_it = efaces.begin(); f_it != efaces.end(); f_it++) {
	    surface_t stype = GetSurfaceType(data->brep->m_F[*f_it].SurfaceOf(), NULL);
	    if (stype != SURFACE_PLANE) {
		connected_faces.push(data->brep->m_F[*f_it].m_face_index);
	    }
	}
	while (!connected_faces.empty()) {
	    int face_index = connected_faces.front();
	    connected_faces.pop();
	    std::set<int> local_edges;
	    std::set<int>::iterator le_it;
	    found_faces.insert(face_index);
	    const ON_BrepFace *face = &(data->brep->m_F[face_index]);
	    const ON_BrepLoop *loop = NULL;
	    // Find the loop in this face that is associated with this subbrep
	    for (int i = 0; i < face->LoopCount(); i++) {
		int loop_ind = face->Loop(i)->m_loop_index;
		if (loops.find(loop_ind) != loops.end()) {
		    loop = &(data->brep->m_L[loop_ind]);
		    break;
		}
	    }
	    // Collect the edges that are partial or skipped
	    for (int i = 0; i < loop->TrimCount(); i++) {
		const ON_BrepTrim *trim = loop->Trim(i);
		ON_BrepEdge *edge = trim->Edge();
		if (edge) {
		    if (partial_edges.find(edge->m_edge_index) != partial_edges.end()) {
			relevant_edges.insert(edge->m_edge_index);
			local_edges.insert(edge->m_edge_index);
		    }
		    if (skip_edges.find(edge->m_edge_index) != skip_edges.end()) {
			relevant_edges.insert(edge->m_edge_index);
			local_edges.insert(edge->m_edge_index);
		    }
		}
	    }
	    // For each collected partial/skipped edge, add any faces not already
	    // found to the queue.
	    for (le_it = local_edges.begin(); le_it != local_edges.end(); le_it++) {
		const ON_BrepEdge *edge = &(data->brep->m_E[*le_it]);
		for (int j = 0; j < edge->TrimCount(); j++) {
		    ON_BrepTrim *trim = edge->Trim(j);
		    if (found_faces.find(trim->Face()->m_face_index) == found_faces.end()) {
			found_faces.insert(trim->Face()->m_face_index);
			connected_faces.push(trim->Face()->m_face_index);
		    }
		}
	    }
	}
	// Build two bounding boxes - one with the new verts in planar_obj, and the other with
	// the edges found above.
	ON_BoundingBox pbb, ebb;
	ON_MinMaxInit(&pbb.m_min, &pbb.m_max);
	ON_MinMaxInit(&ebb.m_min, &ebb.m_max);
	for (int i = 0; i < data->planar_obj->local_brep->m_V.Count(); i++) {
	    const ON_BrepVertex *v = &(data->planar_obj->local_brep->m_V[i]);
	    pbb.Set(v->Point(), true);
	}
	for (re_it = relevant_edges.begin(); re_it != relevant_edges.end(); re_it++) {
	    const ON_BrepEdge *e = &(data->brep->m_E[*re_it]);
	    ON_BoundingBox cbb = e->EdgeCurveOf()->BoundingBox();
	    ebb.Set(cbb.m_min, true);
	    ebb.Set(cbb.m_max, true);
	}
	//std::cout << "in pbb.s rpp " << pout(pbb.m_min) << " " << pout(pbb.m_max) << "\n";
	//std::cout << "in ebb.s rpp " << pout(ebb.m_min) << " " << pout(ebb.m_max) << "\n";

	if (ebb.Includes(pbb)) {
	    bu_log("negative volume\n");
	    data->planar_obj->negative_shape = -1;
	} else {
	    bu_log("positive volume\n");
	    data->planar_obj->negative_shape = 1;
	}
	data->planar_obj->params->bool_op = (data->planar_obj->negative_shape == -1) ? '-' : 'u';
    }

    // Need to preserve the vertex map for this, since we're not done building up the brep
    map_to_array(&(data->planar_obj->planar_obj_vert_map), &(data->planar_obj->planar_obj_vert_cnt), &vertex_map);

    data->planar_obj->local_brep->SetTrimTypeFlags(true);

}
Example #9
0
void Body::HandleModelString(ArgList* pArgList)
{
    if (!g_pLTServer || !pArgList || !pArgList->argv || pArgList->argc == 0) return;

	char* pKey = pArgList->argv[0];
	if (!pKey) return;

	LTBOOL bSlump = !_stricmp(pKey, s_szKeyNoise);
	LTBOOL bLand = !_stricmp(pKey, s_szKeyLand);

	if ( bSlump || bLand )
	{
		// Hitting the ground noise
/*
		SurfaceType eSurface = ST_UNKNOWN;
		CollisionInfo Info;
        g_pLTServer->GetStandingOn(m_hObject, &Info);

		if (Info.m_hPoly && Info.m_hPoly != INVALID_HPOLY)
		{
			eSurface = (SurfaceType)GetSurfaceType(Info.m_hPoly);
		}
		else if (Info.m_hObject) // Get the texture flags from the object...
		{
			eSurface = (SurfaceType)GetSurfaceType(Info.m_hObject);
		}
		else
		{
			return;
		}
*/

		LTVector vPos;
        g_pLTServer->GetObjectPos(m_hObject, &vPos);

		IntersectQuery IQuery;
		IntersectInfo IInfo;

		IQuery.m_From = vPos;
		IQuery.m_To = vPos - LTVector(0,96,0);
		IQuery.m_Flags = INTERSECT_OBJECTS | INTERSECT_HPOLY | IGNORE_NONSOLID;
		IQuery.m_FilterFn = GroundFilterFn;

		SurfaceType eSurface;

		g_cIntersectSegmentCalls++;
        if (g_pLTServer->IntersectSegment(&IQuery, &IInfo))
		{
			if (IInfo.m_hPoly && IInfo.m_hPoly != INVALID_HPOLY)
			{
				eSurface = (SurfaceType)GetSurfaceType(IInfo.m_hPoly);
			}
			else if (IInfo.m_hObject) // Get the texture flags from the object...
			{
				eSurface = (SurfaceType)GetSurfaceType(IInfo.m_hObject);
			}
			else
			{
				return;
			}
		}
		else
		{
			return;
		}

		SURFACE* pSurf = g_pSurfaceMgr->GetSurface(eSurface);
		_ASSERT(pSurf);
		if (!pSurf) return;

		// Update the noise info. We use a time in the future so AI's don't *instantly* react to the sound
		// AI's twice as sensitive to landing sound (because it's louder)

        m_DeathScene.SetNoise(pSurf->fDeathNoiseModifier * (bLand ? 2.0f : 1.0f), g_pLTServer->GetTime() + GetRandom(0.5f, 1.0f));
	}
}
// Criteria:
//
// 1.  A linear edge associated with a planar face == 0
// 2.  All linear edges associated with non-planar faces are part
//     of the same CSG shape AND all non-linear edges' non-planar faces
//     are also part of that same CSG shape = 1
// 3.  If not 2, 0
int
characterize_vert(struct subbrep_object_data *data, const ON_BrepVertex *v)
{
    std::set<int> non_planar_faces;
    std::set<int>::iterator f_it;
    std::set<struct filter_obj *> fobjs;
    std::set<struct filter_obj *>::iterator fo_it;
    struct filter_obj *control = NULL;

    for (int i = 0; i < v->m_ei.Count(); i++) {
	std::set<int> efaces;
	const ON_BrepEdge *edge = &(data->brep->m_E[v->m_ei[i]]);
	ON_Curve *tc = edge->EdgeCurveOf()->Duplicate();
	int is_linear = tc->IsLinear();
	delete tc;
	// Get the faces associated with the edge
	for (int j = 0; j < edge->TrimCount(); j++) {
	    ON_BrepTrim *trim = edge->Trim(j);
	    efaces.insert(trim->Face()->m_face_index);
	}
	for(f_it = efaces.begin(); f_it != efaces.end(); f_it++) {
	    struct filter_obj *new_obj;
	    BU_GET(new_obj, struct filter_obj);
	    surface_t stype = GetSurfaceType(data->brep->m_F[*f_it].SurfaceOf(), new_obj);
	    if (stype == SURFACE_PLANE) {
		filter_obj_free(new_obj);
		BU_PUT(new_obj, struct filter_obj);
		if (is_linear) {
		    for (fo_it = fobjs.begin(); fo_it != fobjs.end(); fo_it++) {
			struct filter_obj *obj = (struct filter_obj *)(*fo_it);
			filter_obj_free(obj);
			BU_PUT(obj, struct filter_obj);
		    }
		    return 0;
		}
	    } else {
		if (!control) {
		    control = new_obj;
		} else {
		    fobjs.insert(new_obj);
		}
	    }
	}
    }

    // Can this happen?
    if (fobjs.size() == 0) {
	if (control) {
	    filter_obj_free(control);
	    BU_PUT(control, struct filter_obj);
	}
	return 0;
    }

    int equal = 1;
    for(fo_it = fobjs.begin(); fo_it != fobjs.end(); fo_it++) {
	if (equal && !filter_objs_equal(*fo_it, control)) equal = 0;
	struct filter_obj *obj = (struct filter_obj *)(*fo_it);
	filter_obj_free(obj);
	BU_PUT(obj, struct filter_obj);
    }
    filter_obj_free(control);
    BU_PUT(control, struct filter_obj);

    return equal;
}
Example #11
0
void CProjectile::AddImpact(DVector vPoint, DVector vNormal, HOBJECT hObj)
{
	// Compute a normal vector
	// Cast a ray to see what we hit
	DVector		tempPos;

	IntersectQuery iq;
	IntersectInfo  ii;
	SurfaceType eType = SURFTYPE_UNKNOWN;
	DBYTE nFX = 0;

	VEC_NORM(m_vDir);
	VEC_MULSCALAR(m_vDir, m_vDir, 2.0f);
	VEC_SUB(iq.m_From, vPoint, m_vDir);
	VEC_MULSCALAR(m_vDir, m_vDir, 10.0f);
	VEC_ADD(iq.m_To, vPoint, m_vDir);
	iq.m_Flags = INTERSECT_OBJECTS | IGNORE_NONSOLID;
	iq.m_FilterFn = NULL;
	iq.m_pUserData = NULL;	

	if (hObj && g_pServerDE->IntersectSegment(&iq, &ii))
	{
		VEC_COPY(vNormal, ii.m_Plane.m_Normal);
		eType = GetSurfaceType(ii.m_hObject, ii.m_hPoly);

		VEC_COPY(tempPos, ii.m_Point);
	}
	else	// Fake it
	{
		VEC_NEGATE(vNormal, m_vDir);

		VEC_COPY(tempPos, vPoint);
	}

	if(m_bExplosion) 
	{
		AddExplosion(tempPos, vNormal);
		nFX |= WFX_SCREENSHAKE;
	}

	if(m_bClientFX)
	{
		CServerDE* pServerDE = BaseClass::GetServerDE();
		if (!pServerDE) return;

/*		ObjectCreateStruct theStruct;
		INIT_OBJECTCREATESTRUCT(theStruct);

		VEC_COPY(theStruct.m_Pos, vPoint);

		HCLASS hClass = pServerDE->GetClass("CClientWeaponSFX");

		CClientWeaponSFX *pWeaponFX = DNULL;

		if(hClass)
			pWeaponFX = (CClientWeaponSFX*)pServerDE->CreateObject(hClass, &theStruct);
*/
		DDWORD	nFX = WFX_SCREENSHAKE;
		DDWORD	nExtras = WFX_EXTRA_DAMAGE;
		WeaponFXExtras	ext;
		ext.fDamage = m_fDamage;

//		if(pWeaponFX)
		SendWeaponSFXMessage(&vPoint, &vPoint, &vNormal, &vNormal, nFX, nExtras, &ext);
	}
}
Example #12
0
void CSpear::HandleImpact(HOBJECT hObj)
{
	if (!g_vtSpearStickPercentage.IsInitted())
	{
        g_vtSpearStickPercentage.Init(g_pLTServer, "SpearStickPercent", LTNULL, 0.9f);
	}

	if (!m_pAmmoData || !m_pAmmoData->pProjectileFX)
	{
		CProjectile::HandleImpact(hObj);
		return;
	}


	CollisionInfo info;
    g_pLTServer->GetLastCollision(&info);

    LTVector vPos, vVel, vCurVel, vP0, vP1;
    g_pLTServer->GetObjectPos(m_hObject, &vPos);

    LTRotation rRot;
    g_pLTServer->GetObjectRotation(m_hObject, &rRot);


	// Should we break the spear?

	enum SpearAction
	{
		eSpearActionBreak,
		eSpearActionStickWorld,
		eSpearActionStickAI,
		eSpearActionStickPlayer,
		eSpearActionStickBody
	};

	SpearAction eSpearAction = eSpearActionBreak;

	// Randomly break even if we could sometimes stick...

	if (GetRandom(0.0, 1.0f) > g_vtSpearStickPercentage.GetFloat())
	{
		eSpearAction = eSpearActionBreak;
	}
	else if (IsMainWorld(hObj))
	{
 		// Calculate where we really hit the world...

		g_pLTServer->GetVelocity(m_hObject, &vVel);

		vP1 = vPos;
        vCurVel = vVel * g_pLTServer->GetFrameTime();
		vP0 = vP1 - vCurVel;
		vP1 += vCurVel;

        LTFLOAT fDot1 = VEC_DOT(info.m_Plane.m_Normal, vP0) - info.m_Plane.m_Dist;
        LTFLOAT fDot2 = VEC_DOT(info.m_Plane.m_Normal, vP1) - info.m_Plane.m_Dist;

		if (fDot1 < 0.0f && fDot2 < 0.0f || fDot1 > 0.0f && fDot2 > 0.0f)
		{
			vPos = vP1;
		}
		else
		{
            LTFLOAT fPercent = -fDot1 / (fDot2 - fDot1);
			VEC_LERP(vPos, vP0, vP1, fPercent);
		}

		// Set our new "real" pos...

        g_pLTServer->SetObjectPos(m_hObject, &vPos);

		eSpearAction = eSpearActionStickWorld;
	}
	else if (IsMoveable(hObj))
	{
		if (IsAI(hObj))
		{
			// Attach to a AI
			eSpearAction = eSpearActionStickAI;
		}
		else if (IsPlayer(hObj))
		{
			// Attach to a Player
			eSpearAction = eSpearActionStickPlayer;
		}
		else if (IsBody(hObj))
		{
			// Attach to a body
 			eSpearAction = eSpearActionStickBody;
		}
		else
		{
			// Could probably come up with a way to attach to moveable
			// non-character objects (like doors), but it is much easier
			// to just break it ;)...

            eSpearAction = eSpearActionBreak;
		}
	}

	// If the surface is too hard, the spear will just break when
	// it hits it...

	SurfaceType eSurf = GetSurfaceType(info);
	SURFACE* pSurf = g_pSurfaceMgr->GetSurface(eSurf);
	if ((eSpearActionBreak == eSpearAction) || ((eSpearActionStickWorld == eSpearAction) && pSurf && pSurf->fHardness > 0.5))
	{
		// Create spear debris...

		DEBRIS* pDebris = g_pDebrisMgr->GetDebris(m_pAmmoData->szName);
		if (pDebris)
		{
			vVel.Norm();
            LTVector vNegVel = -vVel;
            CreatePropDebris(vPos, vNegVel, pDebris->nId);
		}

		CProjectile::HandleImpact(hObj);
		return;
	}

	// Create the Spear powerup...

	char szSpawn[512];
	sprintf(szSpawn, "AmmoBox AmmoType1 %s;AmmoCount1 1;Filename %s;Skin %s",
		m_pAmmoData->szName, m_pAmmoData->pProjectileFX->szModel,
		m_pAmmoData->pProjectileFX->szSkin);

	LTVector vScale = m_pAmmoData->pProjectileFX->vModelScale;

	// Make sure the spear sticks out a little ways...

	vVel.Norm();
	vPos -= (vVel * vScale.z/2.0f);

	if (eSpearActionStickWorld == eSpearAction)
	{
		g_pLTServer->AlignRotation(&rRot, &vVel, LTNULL);
	}

	BaseClass* pClass = SpawnObject(szSpawn, LTVector(-10000,-10000,-10000), rRot);

	if (pClass)
	{
		g_pLTServer->ScaleObject(pClass->m_hObject, &vScale);

		LTVector vDims;
		g_pLTServer->GetObjectDims(pClass->m_hObject, &vDims);
		vDims.x *= vScale.x;
		vDims.y *= vScale.y;
		vDims.z *= vScale.z;

		g_pLTServer->SetObjectDims(pClass->m_hObject, &vDims);

		// We don't want other projectiles to impact on us...

		//uint32 dwUsrFlags = g_pLTServer->GetObjectUserFlags(pClass->m_hObject);
		//dwUsrFlags |= USRFLG_IGNORE_PROJECTILES;
		//g_pLTServer->SetObjectUserFlags(pClass->m_hObject, dwUsrFlags);


		if ( eSpearActionStickAI == eSpearAction || eSpearActionStickPlayer == eSpearAction )
		{
			g_pLTServer->SetObjectUserFlags(pClass->m_hObject, g_pLTServer->GetObjectUserFlags(pClass->m_hObject) & ~USRFLG_GLOW);
			g_pLTServer->SetObjectFlags(pClass->m_hObject, g_pLTServer->GetObjectFlags(pClass->m_hObject) & ~FLAG_TOUCH_NOTIFY);

			if ( eSpearActionStickPlayer == eSpearAction )
			{
				g_pLTServer->SetObjectUserFlags(pClass->m_hObject, g_pLTServer->GetObjectUserFlags(pClass->m_hObject) | USRFLG_ATTACH_HIDE1SHOW3);
			}

			// Attach it to the character

			CCharacter* pCharacter = (CCharacter*)g_pLTServer->HandleToObject(hObj);
			pCharacter->AddSpear(pClass->m_hObject, pCharacter->GetModelNodeLastHit(), rRot);
		}
		else if ( eSpearActionStickBody == eSpearAction )
		{
			g_pLTServer->SetObjectUserFlags(pClass->m_hObject, g_pLTServer->GetObjectUserFlags(pClass->m_hObject) & ~USRFLG_GLOW);
			g_pLTServer->SetObjectFlags(pClass->m_hObject, g_pLTServer->GetObjectFlags(pClass->m_hObject) & ~FLAG_TOUCH_NOTIFY);

			// Attach it to the body

			Body* pBody = (Body*)g_pLTServer->HandleToObject(hObj);
			pBody->AddSpear(pClass->m_hObject, rRot);
		}
		else // ( eSpearActionStickWorld == eSpearAction )
		{
			// Move it to the right position in the world
			g_pLTServer->SetObjectPos(pClass->m_hObject, &vPos);
		}
	}

	CProjectile::HandleImpact(hObj);
}
Example #13
0
void CGrenade::HandleImpact(HOBJECT hObj)
{
	if (!g_vtGrenadeDampenPercent.IsInitted())
	{
        g_vtGrenadeDampenPercent.Init(g_pLTServer, "GrenadeDampenPercent", LTNULL, DEFAULT_GRENADE_DAMPEN_PERCENT);
	}

	if (!g_vtGrenadeMinVelMag.IsInitted())
	{
        g_vtGrenadeMinVelMag.Init(g_pLTServer, "GrenadeMinVelMag", LTNULL, DEFAULT_GRENADE_MIN_VELOCITY);
	}


    LTVector vVel;
    g_pLTServer->GetVelocity(m_hObject, &vVel);


	// See if we are impacting on liquid...

    LTBOOL bEnteringLiquid = LTFALSE;
    uint16 code;
    if (g_pLTServer->GetContainerCode(hObj, &code))
	{
		if (IsLiquid((ContainerCode)code))
		{
            bEnteringLiquid = LTTRUE;
		}
	}


	CollisionInfo info;
    g_pLTServer->GetLastCollision(&info);


	// Do the bounce, if the object we hit isn't liquid...

	if (!bEnteringLiquid)
	{
		vVel += (info.m_vStopVel * 2.0f);
	}


	// Dampen the grenade's new velocity based on the surface type...

    LTFLOAT fDampenPercent = g_vtGrenadeDampenPercent.GetFloat();

	m_eLastHitSurface = GetSurfaceType(info);
	SURFACE* pSurf = g_pSurfaceMgr->GetSurface(m_eLastHitSurface);
	if (pSurf)
	{
		// Play a bounce sound (based on the surface type) if one isn't
		// already playing...

		if ( ShouldPlayBounceSound(pSurf) )
		{
			// Only play one sound at a time...

			if (m_hBounceSnd)
			{
                g_pLTServer->KillSound(m_hBounceSnd);
                m_hBounceSnd = LTNULL;
			}

            uint32 dwFlags = PLAYSOUND_GETHANDLE | PLAYSOUND_TIME;

			int nVolume	= IsLiquid(m_eContainerCode) ? 50 : 100;

            LTVector vPos;
            g_pLTServer->GetObjectPos(m_hObject, &vPos);

			m_hBounceSnd = g_pServerSoundMgr->PlaySoundFromPos(vPos, (char*)GetBounceSound(pSurf),
				pSurf->fGrenadeSndRadius, SOUNDPRIORITY_MISC_MEDIUM, dwFlags, nVolume);
		}

		fDampenPercent = (1.0f - pSurf->fHardness);
	}

	fDampenPercent = fDampenPercent > 1.0f ? 1.0f : (fDampenPercent < 0.0f ? 0.0f : fDampenPercent);

	vVel *= (1.0f - fDampenPercent);


	// See if we should come to a rest...

    LTVector vTest = vVel;
	vTest.y = 0.0f;

	if (vTest.Mag() < g_vtGrenadeMinVelMag.GetFloat())
	{
		// If we're on the ground (or an object), stop movement...

		CollisionInfo standingInfo;
        g_pLTServer->GetStandingOn(m_hObject, &standingInfo);

		CollisionInfo* pInfo = standingInfo.m_hObject ? &standingInfo : &info;

		if (pInfo->m_hObject)
		{
			// Don't stop on walls...

			if (pInfo->m_Plane.m_Normal.y > 0.75f)
			{
				vVel.Init();

				// Turn off gravity, solid, and touch notify....

                uint32 dwFlags = g_pLTServer->GetObjectFlags(m_hObject);
				dwFlags &= ~(FLAG_GRAVITY | FLAG_TOUCH_NOTIFY | FLAG_SOLID);

                g_pLTServer->SetObjectFlags(m_hObject, dwFlags);

				// Rotate to rest...

				RotateToRest();
			}
		}
	}


	// Reset rotation velocities due to the bounce...

	ResetRotationVel(&vVel, pSurf);


	// We need to subtact this out because the engine will add it back in,
	// kind of a kludge but necessary...

	vVel -= info.m_vStopVel;


    g_pLTServer->SetVelocity(m_hObject, &vVel);

	m_cBounces++;
}
void CBodyFX::OnModelKey(HLOCALOBJ hObj, ArgList *pArgs)
{
	if (!m_hServerObject || !hObj || !pArgs || !pArgs->argv || pArgs->argc == 0) return;

	char* pKey = pArgs->argv[0];
	if (!pKey) return;

	LTBOOL bSlump = !_stricmp(pKey, "NOISE");
	LTBOOL bLand = !_stricmp(pKey, "LAND");

	if ( bSlump || bLand )
	{
		LTVector vPos;
        g_pLTClient->GetObjectPos(m_hServerObject, &vPos);

		IntersectQuery IQuery;
		IntersectInfo IInfo;

		IQuery.m_From = vPos;
		IQuery.m_To = vPos - LTVector(0,96,0);
		IQuery.m_Flags = INTERSECT_OBJECTS | INTERSECT_HPOLY | IGNORE_NONSOLID;
		IQuery.m_FilterFn = GroundFilterFn;

		SurfaceType eSurface;

        if (g_pLTClient->IntersectSegment(&IQuery, &IInfo))
		{
			if (IInfo.m_hPoly && IInfo.m_hPoly != INVALID_HPOLY)
			{
				eSurface = (SurfaceType)GetSurfaceType(IInfo.m_hPoly);
			}
			else if (IInfo.m_hObject) // Get the texture flags from the object...
			{
				eSurface = (SurfaceType)GetSurfaceType(IInfo.m_hObject);
			}
			else
			{
				return;
			}
		}
		else
		{
			return;
		}

		// Play the noise

		SURFACE* pSurf = g_pSurfaceMgr->GetSurface(eSurface);
		_ASSERT(pSurf);
		if (!pSurf) return;

		if (bSlump && pSurf->szBodyFallSnd[0])
		{
			g_pClientSoundMgr->PlaySoundFromPos(vPos, pSurf->szBodyFallSnd, pSurf->fBodyFallSndRadius, SOUNDPRIORITY_MISC_LOW);
		}
		else if (bLand && pSurf->szBodyLedgeFallSnd[0])
		{
			g_pClientSoundMgr->PlaySoundFromPos(vPos, pSurf->szBodyLedgeFallSnd, pSurf->fBodyLedgeFallSndRadius, SOUNDPRIORITY_MISC_LOW);
		}
	}
}