feat: complete native client rendering parity updates

This commit is contained in:
shen
2026-09-27 19:44:41 -07:00
parent 9a9dc6d224
commit 25dd65f2ce
99 changed files with 21121 additions and 921 deletions
@@ -9,31 +9,41 @@ void CGrannyModelInstance::MakeBoundBox(TBoundBox* pBoundBox,
D3DXVECTOR3* vtMin,
D3DXVECTOR3* vtMax)
{
pBoundBox->sx = OBBMin[0] * mat[0] + OBBMin[1] * mat[4] + OBBMin[2] * mat[8] + mat[12];
pBoundBox->sy = OBBMin[0] * mat[1] + OBBMin[1] * mat[5] + OBBMin[2] * mat[9] + mat[13];
pBoundBox->sz = OBBMin[0] * mat[2] + OBBMin[1] * mat[6] + OBBMin[2] * mat[10] + mat[14];
pBoundBox->sx = +10000000.0f;
pBoundBox->sy = +10000000.0f;
pBoundBox->sz = +10000000.0f;
pBoundBox->ex = -10000000.0f;
pBoundBox->ey = -10000000.0f;
pBoundBox->ez = -10000000.0f;
pBoundBox->ex = OBBMax[0] * mat[0] + OBBMax[1] * mat[4] + OBBMax[2] * mat[8] + mat[12];
pBoundBox->ey = OBBMax[0] * mat[1] + OBBMax[1] * mat[5] + OBBMax[2] * mat[9] + mat[13];
pBoundBox->ez = OBBMax[0] * mat[2] + OBBMax[1] * mat[6] + OBBMax[2] * mat[10] + mat[14];
for (int c = 0; c < 8; ++c)
{
const float ox = (c & 1) ? OBBMax[0] : OBBMin[0];
const float oy = (c & 2) ? OBBMax[1] : OBBMin[1];
const float oz = (c & 4) ? OBBMax[2] : OBBMin[2];
const float tx = ox * mat[0] + oy * mat[4] + oz * mat[8] + mat[12];
const float ty = ox * mat[1] + oy * mat[5] + oz * mat[9] + mat[13];
const float tz = ox * mat[2] + oy * mat[6] + oz * mat[10] + mat[14];
pBoundBox->sx = min(pBoundBox->sx, tx);
pBoundBox->sy = min(pBoundBox->sy, ty);
pBoundBox->sz = min(pBoundBox->sz, tz);
pBoundBox->ex = max(pBoundBox->ex, tx);
pBoundBox->ey = max(pBoundBox->ey, ty);
pBoundBox->ez = max(pBoundBox->ez, tz);
}
vtMin->x = min(vtMin->x, pBoundBox->sx);
vtMin->x = min(vtMin->x, pBoundBox->ex);
vtMin->y = min(vtMin->y, pBoundBox->sy);
vtMin->y = min(vtMin->y, pBoundBox->ey);
vtMin->z = min(vtMin->z, pBoundBox->sz);
vtMin->z = min(vtMin->z, pBoundBox->ez);
vtMax->x = max(vtMax->x, pBoundBox->sx);
vtMax->x = max(vtMax->x, pBoundBox->ex);
vtMax->y = max(vtMax->y, pBoundBox->sy);
vtMax->y = max(vtMax->y, pBoundBox->ey);
vtMax->z = max(vtMax->z, pBoundBox->sz);
vtMax->z = max(vtMax->z, pBoundBox->ez);
}
bool CGrannyModelInstance::Intersect(const D3DXMATRIX * c_pMatrix,
float * /*pu*/, float * /*pv*/, float * pt)
float * pu, float * pv, float * pt)
{
if (!m_pgrnModelInstance)
return false;
@@ -88,7 +98,51 @@ bool CGrannyModelInstance::Intersect(const D3DXMATRIX * c_pMatrix,
return ret;
}
return true;
bool hasVolumeBone = false;
for (int m = 0; m < meshCount; ++m)
{
const granny_mesh * pgrnMesh = m_pModel->GetGrannyModelPointer()->MeshBindings[m].Mesh;
int * boneIndices = __GetMeshBoneIndices(m);
for (int b = 0; b < pgrnMesh->BoneBindingCount; ++b)
{
const granny_bone_binding& rgrnBoneBinding = pgrnMesh->BoneBindings[b];
if (rgrnBoneBinding.OBBMin[0] >= rgrnBoneBinding.OBBMax[0] &&
rgrnBoneBinding.OBBMin[1] >= rgrnBoneBinding.OBBMax[1] &&
rgrnBoneBinding.OBBMin[2] >= rgrnBoneBinding.OBBMax[2])
continue;
hasVolumeBone = true;
const D3DXMATRIX * pBoneMat = (const D3DXMATRIX *)GrannyGetWorldPose4x4(__GetWorldPosePtr(), boneIndices[b]);
D3DXMATRIX matBoneWorld;
if (c_pMatrix)
D3DXMatrixMultiply(&matBoneWorld, pBoneMat, c_pMatrix);
else
matBoneWorld = *pBoneMat;
if (IntersectCube(&matBoneWorld,
rgrnBoneBinding.OBBMin[0], rgrnBoneBinding.OBBMin[1], rgrnBoneBinding.OBBMin[2],
rgrnBoneBinding.OBBMax[0], rgrnBoneBinding.OBBMax[1], rgrnBoneBinding.OBBMax[2],
ms_vtPickRayOrig, ms_vtPickRayDir,
&u, &v, &t))
{
if (pu) *pu = u;
if (pv) *pv = v;
if (pt) *pt = t;
return true;
}
}
}
if (!hasVolumeBone)
{
if (pu) *pu = u;
if (pv) *pv = v;
if (pt) *pt = t;
return true;
}
return false;
/*
TBoundBox* boundBoxs = s_boundBoxPool.base();
+12 -1
View File
@@ -92,7 +92,16 @@ void CCamera::Wheel(int nLen)
if (IsLock())
return;
m_v3AngularVelocity.y = (float)(nLen) * m_fResistance;
float fAdd = (float)(nLen) * m_fResistance;
if ((m_v3AngularVelocity.y > 0.0f && fAdd < 0.0f) || (m_v3AngularVelocity.y < 0.0f && fAdd > 0.0f))
m_v3AngularVelocity.y = fAdd;
else
m_v3AngularVelocity.y += fAdd;
if (m_v3AngularVelocity.y > 500.0f)
m_v3AngularVelocity.y = 500.0f;
else if (m_v3AngularVelocity.y < -500.0f)
m_v3AngularVelocity.y = -500.0f;
}
void CCamera::BeginDrag(int nMouseX, int nMouseY)
@@ -167,6 +176,8 @@ bool CCamera::Drag(int nMouseX, int nMouseY, LPPOINT lpReturnPoint)
m_v3AngularVelocity.x = fNewRotationVelocity;
m_v3AngularVelocity.z = fNewPitchVelocity;
m_lMousePosX = lMouseX;
m_lMousePosY = lMouseY;
lpReturnPoint->x = m_lMousePosX;
lpReturnPoint->y = m_lMousePosY;
return true;
@@ -0,0 +1,176 @@
#include "StdAfx.h"
#include "CullingManager.h"
#include "GrpObjectInstance.h"
//#define COUNT_SHOWING_SPHERE
#ifdef COUNT_SHOWING_SPHERE
int showingcount = 0;
#endif
void CCullingManager::RayTraceCallback(const Vector3d &/*p1*/, // source pos of ray
const Vector3d &/*dir*/, // dest pos of ray
float distance,
const Vector3d &/*sect*/,
SpherePack *sphere)
{
//if (state!=VS_OUTSIDE)
//{
if (m_RayFarDistance<=0.0f || m_RayFarDistance>=distance)
{
#ifdef SPHERELIB_STRICT
if (sphere->IS_SPHERE)
puts("CCullingManager::RayTraceCallback");
#endif
m_list.push_back((CGraphicObjectInstance *)sphere->GetUserData());
}
//f((CGraphicObjectInstance *)sphere->GetUserData());
//}
}
void CCullingManager::VisibilityCallback(const Frustum &/*f*/,SpherePack *sphere,ViewState state)
{
#ifdef SPHERELIB_STRICT
if (sphere->IS_SPHERE)
puts("CCullingManager::VisibilityCallback");
#endif
CGraphicObjectInstance * pInstance = (CGraphicObjectInstance*)sphere->GetUserData();
/*if (state == VS_PARTIAL)
{
Vector3d v;
float r;
pInstance->GetBoundingSphere(v,r);
state = f.ViewVolumeTest(v,r);
}*/
if (state == VS_OUTSIDE)
{
#ifdef COUNT_SHOWING_SPHERE
if (pInstance->isShow())
{
Tracef("SH : %p ",sphere->GetUserData());
showingcount--;
Tracef("show size : %5d\n",showingcount);
}
#endif
pInstance->Hide();
}
else
{
#ifdef COUNT_SHOWING_SPHERE
if (!pInstance->isShow())
{
Tracef("HS : %p ",sphere->GetUserData());
showingcount++;
Tracef("show size : %5d\n",showingcount);
}
#endif
pInstance->Show();
}
}
void CCullingManager::RangeTestCallback(const Vector3d &/*p*/,float /*distance*/,SpherePack *sphere,ViewState state)
{
#ifdef SPHERELIB_STRICT
if (sphere->IS_SPHERE)
puts("CCullingManager::RangeTestCallback");
#endif
if (state!=VS_OUTSIDE)
{
m_list.push_back((CGraphicObjectInstance *)sphere->GetUserData());
//f((CGraphicObjectInstance *)sphere->GetUserData());
}
//assert(false && "NOT REACHED");
}
void CCullingManager::Reset()
{
m_Factory->Reset();
}
void CCullingManager::Update()
{
// TODO : update each object
// ÇÏÁö¸»°í °¢ÀÚ ÇÏ°Ô ÇØº¸ÀÚ
//DWORD time = ELTimer_GetMSec();
//Reset();
m_Factory->Process();
//Tracef("cull update : %3d ",ELTimer_GetMSec()-time);
}
void CCullingManager::Process()
{
//DWORD time = ELTimer_GetMSec();
//Frustum f;
UpdateViewMatrix();
UpdateProjMatrix();
BuildViewFrustum();
m_Factory->FrustumTest(GetFrustum(), this);
//Tracef("cull process : %3d ",ELTimer_GetMSec()-time);
}
CCullingManager::CullingHandle CCullingManager::Register(CGraphicObjectInstance * obj)
{
assert(obj);
#ifdef COUNT_SHOWING_SPHERE
Tracef("CR : %p ",obj);
showingcount++;
Tracef("show size : %5d\n",showingcount);
#endif
Vector3d center;
float radius;
obj->GetBoundingSphere(center,radius);
return m_Factory->AddSphere_(center,radius,obj, false);
}
void CCullingManager::Unregister(CullingHandle h)
{
#ifdef COUNT_SHOWING_SPHERE
if (((CGraphicObjectInstance*)h->GetUserData())->isShow())
{
Tracef("DE : %p ",h->GetUserData());
showingcount--;
Tracef("show size : %5d\n",showingcount);
}
#endif
m_Factory->Remove(h);
}
CCullingManager::CCullingManager()
{
m_Factory = new SpherePackFactory(
10000, // maximum count
6400, // root radius
1600, // leaf radius
400 // extra radius
);
}
CCullingManager::~CCullingManager()
{
delete m_Factory;
}
void CCullingManager::FindRange(const Vector3d &p, float radius)
{
m_list.clear();
m_Factory->RangeTest(p, radius, this);
}
void CCullingManager::FindRay(const Vector3d &p1, const Vector3d &dir)
{
m_RayFarDistance = -1;
m_list.clear();
m_Factory->RayTrace(p1,dir,this);
}
void CCullingManager::FindRayDistance(const Vector3d &p1, const Vector3d &dir, float distance)
{
m_RayFarDistance = distance;
m_list.clear();
m_Factory->RayTrace(p1,dir,this);
}
+4
View File
@@ -139,6 +139,10 @@ void CGraphicBase::SetBackBufferSize(UINT uWidth, UINT uHeight)
{
ms_d3dPresentParameter.BackBufferWidth = uWidth;
ms_d3dPresentParameter.BackBufferHeight = uHeight;
ms_iWidth = uWidth;
ms_iHeight = uHeight;
ms_Viewport.Width = uWidth;
ms_Viewport.Height = uHeight;
}
void CGraphicBase::SetDefaultIndexBuffer(UINT eDefIB)
@@ -723,13 +723,32 @@ namespace UI
CWindow * pLayer = *ritor;
CWindow * pPickedWindow = pLayer->PickWindow(x, y);
if (pPickedWindow != pLayer)
if (pPickedWindow != pLayer) {
return pPickedWindow;
}
}
return NULL;
}
bool CWindowManager::IsPointInsideActiveUI(long x, long y)
{
CWindow * pPicked = __PickWindow(x, y);
if (!pPicked || pPicked == m_pRootWindow)
return false;
CWindow * pCur = pPicked;
while (pCur->GetParent() && pCur->GetParent() != m_pRootWindow)
{
pCur = pCur->GetParent();
}
if (pCur && (0 == strcmp(pCur->GetName(), "GAME") || pCur == m_pRootWindow))
return false;
return true;
}
void CWindowManager::SetMousePosition(long x, long y)
{
if (m_iHres==0)
@@ -760,6 +779,13 @@ namespace UI
SetMousePosition(x, y);
CWindow * pPointWindow = __PickWindow(m_lMouseX, m_lMouseY);
if (x == 470 && y == 380) {
std::printf(">>> C++ RunMouseMove(470, 380): m_lMouse=(%ld, %ld), pPointWindow=%s (type=%s, is_show=%d)\n",
m_lMouseX, m_lMouseY,
pPointWindow ? pPointWindow->GetName() : "NULL",
pPointWindow ? typeid(*pPointWindow).name() : "none",
pPointWindow ? pPointWindow->IsShow() : 0);
}
if (g_bShowOverInWindowName)
{
@@ -103,6 +103,9 @@ namespace UI
void ActivateWindow(CWindow * pWin);
void DeactivateWindow();
CWindow * GetActivateWindow();
bool IsPointInsideActiveUI(long x, long y);
CWindow * PickWindow(long x, long y) { return __PickWindow(x, y); }
void SetTop(CWindow * pWin);
void SetTopUIWindow();
void ResetCapture();
+1 -1
View File
@@ -250,7 +250,7 @@ bool CActorInstance::IsHandMode()
bool CActorInstance::IsTwoHandMode()
{
if (CRaceMotionData::MODE_TWOHAND_SWORD==GetMotionMode())
if (CRaceMotionData::MODE_TWOHAND_SWORD==GetMotionMode() || CRaceMotionData::MODE_HORSE_TWOHAND_SWORD==GetMotionMode())
return true;
return false;
+6
View File
@@ -0,0 +1,6 @@
// stdafx.cpp : source file that includes just the standard includes
// SphereLib.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "StdAfx.h"
+93
View File
@@ -0,0 +1,93 @@
/* Copyright (C) John W. Ratcliff, 2001.
* All rights reserved worldwide.
*
* This software is provided "as is" without express or implied
* warranties. You may freely copy and compile this source into
* applications you distribute provided that the copyright text
* below is included in the resulting source code, for example:
* "Portions Copyright (C) John W. Ratcliff, 2001"
*/
#include "StdAfx.h"
#include "frustum.h"
//#include "frustum.h"
/*void Frustum::Set(int x1,int y1,int x2,int y2)
{
mX1 = x1;
mY1 = y1;
mX2 = x2;
mY2 = y2;
}
*/
ViewState Frustum::ViewVolumeTest(const Vector3d &c_v3Center,const float c_fRadius) const
{
if (m_bUsingSphere)
{
D3DXVECTOR3 v(
c_v3Center.x-m_v3Center.x,
c_v3Center.y-m_v3Center.y,
c_v3Center.z-m_v3Center.z);
if ((c_fRadius + m_fRadius) * (c_fRadius + m_fRadius) < D3DXVec3LengthSq(&v))
{
return VS_OUTSIDE;
}
}
const int count=6;
D3DXVECTOR3 center = c_v3Center;
//center.y *=-1;
int i;
float distance[count];
for(i=0;i<count;i++)
{
distance[i] = D3DXPlaneDotCoord(&m_plane[i],&center);
if (distance[i]<=-c_fRadius)
return VS_OUTSIDE;
}
//return VS_INSIDE;
for(i=0;i<count;i++)
{
if (distance[i]<=c_fRadius)
return VS_PARTIAL;
}
return VS_INSIDE;
}
void Frustum::BuildViewFrustum(D3DXMATRIX & mat)
{
m_bUsingSphere = false;
m_plane[0] = D3DXPLANE( mat._13, mat._23, mat._33, mat._43);
m_plane[1] = D3DXPLANE(mat._14 - mat._13, mat._24 - mat._23, mat._34 - mat._33, mat._44 - mat._43);
//m_plane[0] = D3DXPLANE(mat._14 + mat._13, mat._24 + mat._23, mat._34 + mat._33, mat._44 + mat._43);
m_plane[2] = D3DXPLANE(mat._14 + mat._11, mat._24 + mat._21, mat._34 + mat._31, mat._44 + mat._41);
m_plane[3] = D3DXPLANE(mat._14 - mat._11, mat._24 - mat._21, mat._34 - mat._31, mat._44 - mat._41);
m_plane[4] = D3DXPLANE(mat._14 + mat._12, mat._24 + mat._22, mat._34 + mat._32, mat._44 + mat._42);
m_plane[5] = D3DXPLANE(mat._14 - mat._12, mat._24 - mat._22, mat._34 - mat._32, mat._44 - mat._42);
for(int i=0;i<6;i++)
D3DXPlaneNormalize(&m_plane[i],&m_plane[i]);
}
void Frustum::BuildViewFrustum2(D3DXMATRIX & mat, float fNear, float fFar, float fFov, float fAspect, const D3DXVECTOR3 & vCamera, const D3DXVECTOR3 & vLook)
{
float fViewLen = fFar-fNear;
float fH = fViewLen * tan(fFov*0.5f);
float fW = fH*fAspect;
D3DXVECTOR3 P(0.0f, 0.0f, fNear+fViewLen*0.5f);
D3DXVECTOR3 Q(fW, fH, fViewLen);
D3DXVECTOR3 PQ = P-Q;
m_fRadius = D3DXVec3Length(&PQ);
m_v3Center = vCamera + vLook * (fNear+fViewLen*0.5f);
BuildViewFrustum(mat);
m_bUsingSphere = true;
}
+248
View File
@@ -0,0 +1,248 @@
/* Copyright (C) John W. Ratcliff, 2001.
* All rights reserved worldwide.
*
* This software is provided "as is" without express or implied
* warranties. You may freely copy and compile this source into
* applications you distribute provided that the copyright text
* below is included in the resulting source code, for example:
* "Portions Copyright (C) John W. Ratcliff, 2001"
*/
#include "StdAfx.h"
#include "sphere.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
bool Vector3d::IsInStaticRange() const
{
const float LIMIT = 3276700.0f;
if (x<LIMIT && x>-LIMIT)
if (y<LIMIT && y>-LIMIT)
if (z<LIMIT && z>-LIMIT)
return true;
return false;
}
void Sphere::Set(const Vector3d &center, float radius)
{
#ifdef __STATIC_RANGE__
assert(center.IsInStaticRange());
#endif
mCenter = center;
mRadius = radius;
mRadius2 = radius*radius;
}
//ray-sphere intersection test from Graphics Gems p.388
// **NOTE** There is a bug in this Graphics Gem. If the origin
// of the ray is *inside* the sphere being tested, it reports the
// wrong intersection location. This code has a fix for the bug.
bool Sphere::RayIntersection(const Vector3d &rayOrigin,
const Vector3d &dir,
Vector3d *intersect)
{
//notation:
//point E = rayOrigin
//point O = sphere center
Vector3d EO = mCenter - rayOrigin;
Vector3d V = dir;
float dist2 = EO.x*EO.x + EO.y*EO.y + EO.z * EO.z;
// Bug Fix For Gem, if origin is *inside* the sphere, invert the
// direction vector so that we get a valid intersection location.
if ( dist2 < mRadius2 ) V*=-1;
float v = EO.Dot(V);
float disc = mRadius2 - (EO.Length2() - v*v);
if (disc > 0.0f)
{
if ( intersect )
{
float d = sqrtf(disc);
//float dist2 = rayOrigin.DistanceSq(mCenter);
*intersect = rayOrigin + V*(v-d);
}
return true;
}
return false;
}
//
bool Sphere::RayIntersection(const Vector3d &rayOrigin,
const Vector3d &V,
float distance,
Vector3d *intersect)
{
Vector3d sect;
bool hit = RayIntersectionInFront(rayOrigin,V,&sect);
if ( hit )
{
float d = rayOrigin.DistanceSq(sect);
if ( d > (distance*distance) ) return false;
if ( intersect ) *intersect = sect;
return true;
}
return false;
}
bool Sphere::RayIntersectionInFront(const Vector3d &rayOrigin,
const Vector3d &V,
Vector3d *intersect)
{
Vector3d sect;
bool hit = RayIntersection(rayOrigin,V,&sect);
if ( hit )
{
Vector3d dir = sect - rayOrigin;
float dot = dir.Dot(V);
if ( dot >= 0 ) // then it's in front!
{
if ( intersect ) *intersect = sect;
return true;
}
}
return false;
}
void Sphere::Report(void)
{
}
/*
An Efficient Bounding Sphere
by Jack Ritter
from "Graphics Gems", Academic Press, 1990
*/
/* Routine to calculate tight bounding sphere over */
/* a set of points in 3D */
/* This contains the routine find_bounding_sphere(), */
/* the struct definition, and the globals used for parameters. */
/* The abs() of all coordinates must be < BIGNUMBER */
/* Code written by Jack Ritter and Lyle Rains. */
#define BIGNUMBER 100000000.0 /* hundred million */
void Sphere::Compute(const SphereInterface &source)
{
Vector3d xmin,xmax,ymin,ymax,zmin,zmax,dia1,dia2;
/* FIRST PASS: find 6 minima/maxima points */
xmin.Set(BIGNUMBER,BIGNUMBER,BIGNUMBER);
xmax.Set(-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
ymin.Set(BIGNUMBER,BIGNUMBER,BIGNUMBER);
ymax.Set(-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
zmin.Set(BIGNUMBER,BIGNUMBER,BIGNUMBER);
zmax.Set(-BIGNUMBER,-BIGNUMBER,-BIGNUMBER);
int count = source.GetVertexCount();
for (int i=0; i<count; i++)
{
Vector3d caller_p;
source.GetVertex(i,caller_p);
if (caller_p.GetX()<xmin.GetX()) xmin = caller_p; /* New xminimum point */
if (caller_p.GetX()>xmax.GetX()) xmax = caller_p;
if (caller_p.GetY()<ymin.GetY()) ymin = caller_p;
if (caller_p.GetY()>ymax.GetY()) ymax = caller_p;
if (caller_p.GetZ()<zmin.GetZ()) zmin = caller_p;
if (caller_p.GetZ()>zmax.GetZ()) zmax = caller_p;
}
/* Set xspan = distance between the 2 points xmin & xmax (squared) */
float dx = xmax.GetX() - xmin.GetX();
float dy = xmax.GetY() - xmin.GetY();
float dz = xmax.GetZ() - xmin.GetZ();
float xspan = dx*dx + dy*dy + dz*dz;
/* Same for y & z spans */
dx = ymax.GetX() - ymin.GetX();
dy = ymax.GetY() - ymin.GetY();
dz = ymax.GetZ() - ymin.GetZ();
float yspan = dx*dx + dy*dy + dz*dz;
dx = zmax.GetX() - zmin.GetX();
dy = zmax.GetY() - zmin.GetY();
dz = zmax.GetZ() - zmin.GetZ();
float zspan = dx*dx + dy*dy + dz*dz;
/* Set points dia1 & dia2 to the maximally separated pair */
dia1 = xmin;
dia2 = xmax; /* assume xspan biggest */
float maxspan = xspan;
if (yspan>maxspan)
{
maxspan = yspan;
dia1 = ymin;
dia2 = ymax;
}
if (zspan>maxspan)
{
dia1 = zmin;
dia2 = zmax;
}
/* dia1,dia2 is a diameter of initial sphere */
/* calc initial center */
mCenter.SetX( (dia1.GetX()+dia2.GetX())*0.5f );
mCenter.SetY( (dia1.GetY()+dia2.GetY())*0.5f );
mCenter.SetZ( (dia1.GetZ()+dia2.GetZ())*0.5f );
/* calculate initial radius**2 and radius */
dx = dia2.GetX()-mCenter.GetX(); /* x component of radius vector */
dy = dia2.GetY()-mCenter.GetY(); /* y component of radius vector */
dz = dia2.GetZ()-mCenter.GetZ(); /* z component of radius vector */
mRadius2 = dx*dx + dy*dy + dz*dz;
mRadius = float(sqrt(mRadius2));
/* SECOND PASS: increment current sphere */
for (int j=0; j<count; j++)
{
Vector3d caller_p;
source.GetVertex(j,caller_p);
dx = caller_p.GetX()-mCenter.GetX();
dy = caller_p.GetY()-mCenter.GetY();
dz = caller_p.GetZ()-mCenter.GetZ();
float old_to_p_sq = dx*dx + dy*dy + dz*dz;
if (old_to_p_sq > mRadius2) /* do r**2 test first */
{ /* this point is outside of current sphere */
float old_to_p = float(sqrt(old_to_p_sq));
/* calc radius of new sphere */
mRadius = (mRadius + old_to_p) * 0.5f;
mRadius2 = mRadius*mRadius; /* for next r**2 compare */
float old_to_new = old_to_p - mRadius;
/* calc center of new sphere */
float recip = 1.0f /old_to_p;
float cx = (mRadius*mCenter.GetX() + old_to_new*caller_p.GetX()) * recip;
float cy = (mRadius*mCenter.GetY() + old_to_new*caller_p.GetY()) * recip;
float cz = (mRadius*mCenter.GetZ() + old_to_new*caller_p.GetZ()) * recip;
mCenter.Set(cx,cy,cz);
}
}
}
+878
View File
@@ -0,0 +1,878 @@
/* Copyright (C) John W. Ratcliff, 2001.
* All rights reserved worldwide.
*
* This software is provided "as is" without express or implied
* warranties. You may freely copy and compile this source into
* applications you distribute provided that the copyright text
* below is included in the resulting source code, for example:
* "Portions Copyright (C) John W. Ratcliff, 2001"
*/
#include "StdAfx.h"
#include "spherepack.h"
#if DEMO
int PrintText(int x, int y, int color, char* output, ...);
int DrawLine(int x1, int y1, int x2, int y2, int color);
int DrawCircle(int locx, int locy, int radius, int color);
#endif
SpherePackFactory::SpherePackFactory(int maxspheres, float rootsize, float leafsize, float gravy)
{
NANOBEGIN
maxspheres *= 4; // include room for both trees, the root node and leaf node tree, and the superspheres
mMaxRootSize = rootsize;
mMaxLeafSize = leafsize;
mSuperSphereGravy = gravy;
mIntegrate = new SpherePackFifo(maxspheres);
mRecompute = new SpherePackFifo(maxspheres);
mSpheres.Set(maxspheres); // init pool to hold all possible SpherePack instances.
Vector3d p(0,0,0);
mRoot = mSpheres.GetFreeLink(); // initially empty
mRoot->Init(this,p,6553600,0, false);
mRoot->SetSpherePackFlag(SpherePackFlag(SPF_SUPERSPHERE | SPF_ROOTNODE | SPF_ROOT_TREE));
#if DEMO
mRoot->SetColor(0x00FFFFFF);
#endif
mLeaf = mSpheres.GetFreeLink();; // initially empty
mLeaf->Init(this,p,1638400,0,false);
mLeaf->SetSpherePackFlag(SpherePackFlag(SPF_SUPERSPHERE | SPF_ROOTNODE | SPF_LEAF_TREE));
#if DEMO
mLeaf->SetColor(0x00FFFFFF);
mColorCount = 0;
mColors[0] = 0x00FF0000;
mColors[1] = 0x0000FF00;
mColors[2] = 0x000000FF;
mColors[3] = 0x00FFFF00;
mColors[4] = 0x00FF00FF;
mColors[5] = 0x0000FFFF;
mColors[6] = 0x00FF8080;
mColors[7] = 0x0000FF80;
mColors[8] = 0x000080FF;
mColors[9] = 0x00FFFF80;
mColors[10] = 0x00FF80FF;
mColors[11] = 0x0080FFFF;
#endif
NANOEND
}
SpherePackFactory::~SpherePackFactory(void)
{
delete mIntegrate; // free up integration fifo
delete mRecompute; // free up recomputation fifo.
}
void SpherePackFactory::Process(void)
{
{
// First recompute anybody that needs to be recomputed!!
// When leaf node spheres exit their parent sphere, then the parent sphere needs to be rebalanced. In fact,it may now be empty and
// need to be removed.
// This is the location where (n) number of spheres in the recomputation FIFO are allowed to be rebalanced in the tree.
int maxrecompute = mRecompute->GetCount();
for (int i = 0; i < maxrecompute; ++i)
{
SpherePack * pack = mRecompute->Pop();
if (!pack) break;
pack->SetFifo1(0); // no longer on the fifo!!
bool kill = pack->Recompute(mSuperSphereGravy);
if (kill) Remove(pack);
}
}
{
// Now, process the integration step.
int maxintegrate = mIntegrate->GetCount();
for (int i = 0; i < maxintegrate; ++i)
{
SpherePack * pack = mIntegrate->Pop();
if (!pack)
break;
pack->SetFifo2(0);
if (pack->HasSpherePackFlag(SPF_ROOT_TREE))
Integrate(pack,mRoot,mMaxRootSize); // integrate this one single dude against the root node.
else
Integrate(pack,mLeaf,mMaxLeafSize); // integrate this one single dude against the root node.
}
}
}
SpherePack * SpherePackFactory::AddSphere_(const Vector3d &pos,
float radius,
void *userdata,
bool isSphere,
int flags)
{
SpherePack *pack = mSpheres.GetFreeLink();
assert(pack);
if (pack)
{
if (flags & SPF_ROOT_TREE)
{
pack->Init(this,pos,radius,userdata, isSphere);
pack->SetSpherePackFlag(SPF_ROOT_TREE); // member of the leaf node tree!
AddIntegrate(pack); // add to integration list.
}
else
{
pack->Init(this,pos,radius,userdata, isSphere);
pack->SetSpherePackFlag(SPF_LEAF_TREE); // member of the leaf node tree!
AddIntegrate(pack); // add to integration list.
}
}
return pack;
}
void SpherePackFactory::AddIntegrate(SpherePack *pack)
{
if (pack->HasSpherePackFlag(SPF_ROOT_TREE))
mRoot->AddChild(pack);
else
mLeaf->AddChild(pack);
pack->SetSpherePackFlag(SPF_INTEGRATE); // still needs to be integrated!
SpherePack **fifo = mIntegrate->Push(pack); // add it to the integration stack.
pack->SetFifo2(fifo);
}
void SpherePackFactory::AddRecompute(SpherePack *recompute)
{
if (!recompute->HasSpherePackFlag(SPF_RECOMPUTE))
{
if (recompute->GetChildCount())
{
recompute->SetSpherePackFlag(SPF_RECOMPUTE); // needs to be recalculated!
SpherePack **fifo = mRecompute->Push(recompute);
recompute->SetFifo1(fifo);
}
else
{
Remove(recompute);
}
}
}
void SpherePackFactory::Render(void)
{
#if DEMO
mRoot->Render(mRoot->GetColor());
mLeaf->Render(mLeaf->GetColor());
#endif
}
void SpherePack::Render(unsigned int /*color*/)
{
#if DEMO
if (!HasSpherePackFlag(SPF_ROOTNODE))
{
if (HasSpherePackFlag(SPF_SUPERSPHERE))
{
color = mColor;
}
else
{
if (mParent->HasSpherePackFlag(SPF_ROOTNODE)) color = 0x00FFFFFF;
}
#if DEMO
DrawCircle(int(mCenter.x), int(mCenter.y),int(GetRadius()),color);
#endif
if (HasSpherePackFlag(SPF_SUPERSPHERE))
{
if (HasSpherePackFlag(SPF_LEAF_TREE))
{
#if DEMO
DrawCircle(int(mCenter.x), int(mCenter.y),int(GetRadius()),color);
#endif
#ifdef SPHERELIB_STRICT
if (!sphere->IS_SPHERE)
puts("SpherePack::Render");
#endif
SpherePack *link = (SpherePack *) GetUserData();
link = link->GetParent();
if (link && !link->HasSpherePackFlag(SPF_ROOTNODE))
{
DrawLine(int(mCenter.x), int(mCenter.y),
int(link->mCenter.x), int(link->mCenter.y),
link->GetColor());
}
}
else
{
#if DEMO
DrawCircle(int(mCenter.x), int(mCenter.y),int(GetRadius())+3,color);
#endif
}
}
}
if (mChildren)
{
SpherePack *pack = mChildren;
while (pack)
{
pack->Render(color);
pack = pack->_GetNextSibling();
}
}
#endif
}
bool SpherePack::Recompute(float gravy)
{
if (!mChildren) return true; // kill it!
if (HasSpherePackFlag(SPF_ROOTNODE)) return false; // don't recompute root nodes!
#if 1
// recompute bounding sphere!
Vector3d total(0,0,0);
int count=0;
SpherePack *pack = mChildren;
while (pack)
{
total+=pack->mCenter;
count++;
pack = pack->_GetNextSibling();
}
if (count)
{
float recip = 1.0f / float(count);
total*=recip;
Vector3d oldpos = mCenter;
#ifdef __STATIC_RANGE__
assert(total.IsInStaticRange());
#endif
mCenter = total; // new origin!
float maxradius = 0;
pack = mChildren;
while (pack)
{
float dist = DistanceSquared(pack);
float radius = sqrtf(dist) + pack->GetRadius();
if (radius > maxradius)
{
maxradius = radius;
if ((maxradius+gravy) >= GetRadius())
{
#ifdef __STATIC_RANGE__
assert(oldpos.IsInStaticRange());
#endif
mCenter = oldpos;
ClearSpherePackFlag(SPF_RECOMPUTE);
return false;
}
}
pack = pack->_GetNextSibling();
}
maxradius+=gravy;
SetRadius(maxradius);
// now all children have to recompute binding distance!!
pack = mChildren;
while (pack)
{
pack->ComputeBindingDistance(this);
pack = pack->_GetNextSibling();
}
}
#endif
ClearSpherePackFlag(SPF_RECOMPUTE);
return false;
}
void SpherePack::LostChild(SpherePack *t)
{
assert(mChildCount);
assert(mChildren);
#ifdef _DEBUG // debug validation code.
SpherePack *pack = mChildren;
bool found = false;
while (pack)
{
if (pack == t)
{
assert(!found);
found = true;
}
pack = pack->_GetNextSibling();
}
assert(found);
#endif
// first patch old linked list.. his previous now points to his next
SpherePack *prev = t->_GetPrevSibling();
if (prev)
{
SpherePack *next = t->_GetNextSibling();
prev->SetNextSibling(next); // my previous now points to my next
if (next) next->SetPrevSibling(prev);
// list is patched!
}
else
{
SpherePack *next = t->_GetNextSibling();
mChildren = next;
if (mChildren) mChildren->SetPrevSibling(0);
}
mChildCount--;
if (!mChildCount && HasSpherePackFlag(SPF_SUPERSPHERE))
{
mFactory->Remove(this);
}
}
void SpherePackFactory::Remove(SpherePack*pack)
{
if (pack->HasSpherePackFlag(SPF_ROOTNODE)) return; // CAN NEVER REMOVE THE ROOT NODE EVER!!!
if (pack->HasSpherePackFlag(SPF_SUPERSPHERE) && pack->HasSpherePackFlag(SPF_LEAF_TREE))
{
#ifdef SPHERELIB_STRICT
if (!pack->IS_SPHERE)
puts("SpherePackFactory::Remove");
#endif
SpherePack *link = (SpherePack *) pack->GetUserData();
Remove(link);
}
pack->Unlink();
mSpheres.Release(pack);
}
#if DEMO
unsigned int SpherePackFactory::GetColor(void)
{
unsigned int ret = mColors[mColorCount];
mColorCount++;
if (mColorCount == MAXCOLORS) mColorCount = 0;
return ret;
}
#endif
void SpherePackFactory::Integrate(SpherePack *pack,
SpherePack *supersphere,
float node_size)
{
// ok..time to integrate this sphere with the tree
// first find which supersphere we are closest to the center of
SpherePack *search = supersphere->GetChildren();
SpherePack *nearest1 = 0; // nearest supersphere we are completely
float neardist1 = 1e38f; // enclosed within
SpherePack *nearest2 = 0; // supersphere we must grow the least to
float neardist2 = 1e38f; // add ourselves to.
//int scount = 1;
while (search)
{
if (search->HasSpherePackFlag(SPF_SUPERSPHERE) && !search->HasSpherePackFlag(SPF_ROOTNODE) && search->GetChildCount())
{
float dist = pack->DistanceSquared(search);
if (nearest1)
{
if (dist < neardist1)
{
float d = sqrtf(dist)+pack->GetRadius();
if (d <= search->GetRadius())
{
neardist1 = dist;
nearest1 = search;
}
}
}
else
{
float d = (sqrtf(dist) + pack->GetRadius())-search->GetRadius();
if (d < neardist2)
{
if (d < 0)
{
neardist1 = dist;
nearest1 = search;
}
else
{
neardist2 = d;
nearest2 = search;
}
}
}
}
search = search->_GetNextSibling();
}
// ok...now..on exit let's see what we got.
if (nearest1)
{
// if we are inside an existing supersphere, we are all good!
// we need to detach item from wherever it is, and then add it to
// this supersphere as a child.
pack->Unlink();
nearest1->AddChild(pack);
pack->ComputeBindingDistance(nearest1);
nearest1->Recompute(mSuperSphereGravy);
if (nearest1->HasSpherePackFlag(SPF_LEAF_TREE))
{
#ifdef SPHERELIB_STRICT
if (!nearest1->IS_SPHERE)
puts("SpherePackFactory::Integrate1");
#endif
SpherePack *link = (SpherePack *) nearest1->GetUserData();
link->NewPosRadius(nearest1->GetPos(), nearest1->GetRadius());
}
}
else
{
bool newsphere = true;
if (nearest2)
{
float newsize = neardist2 + nearest2->GetRadius() + mSuperSphereGravy;
if (newsize <= node_size)
{
pack->Unlink();
nearest2->SetRadius(newsize);
nearest2->AddChild(pack);
nearest2->Recompute(mSuperSphereGravy);
pack->ComputeBindingDistance(nearest2);
if (nearest2->HasSpherePackFlag(SPF_LEAF_TREE))
{
#ifdef SPHERELIB_STRICT
if (!nearest2->IS_SPHERE)
puts("SpherePackFactory::Integrate2");
#endif
SpherePack *link = (SpherePack *) nearest2->GetUserData();
link->NewPosRadius(nearest2->GetPos(), nearest2->GetRadius());
}
newsphere = false;
}
}
if (newsphere)
{
assert(supersphere->HasSpherePackFlag(SPF_ROOTNODE));
// we are going to create a new superesphere around this guy!
pack->Unlink();
SpherePack *parent = mSpheres.GetFreeLink();
assert(parent);
parent->Init(this, pack->GetPos(), pack->GetRadius()+mSuperSphereGravy, 0, false);
if (supersphere->HasSpherePackFlag(SPF_ROOT_TREE))
parent->SetSpherePackFlag(SPF_ROOT_TREE);
else
parent->SetSpherePackFlag(SPF_LEAF_TREE);
parent->SetSpherePackFlag(SPF_SUPERSPHERE);
#if DEMO
parent->SetColor(GetColor());
#endif
parent->AddChild(pack);
supersphere->AddChild(parent);
parent->Recompute(mSuperSphereGravy);
pack->ComputeBindingDistance(parent);
if (parent->HasSpherePackFlag(SPF_LEAF_TREE))
{
// need to create parent association!
SpherePack *link = AddSphere_(parent->GetPos(), parent->GetRadius(), parent, true, SPF_ROOT_TREE);
parent->SetUserData(link, true); // hook him up!!
}
}
}
pack->ClearSpherePackFlag(SPF_INTEGRATE); // we've been integrated!
}
void SpherePackFactory::FrustumTest(const Frustum &f,SpherePackCallback *callback)
{
// test case here, just traverse children.
mCallback = callback;
mRoot->VisibilityTest(f,this,VS_PARTIAL);
}
void SpherePack::VisibilityTest(const Frustum &f,SpherePackCallback *callback,ViewState state)
{
if (state == VS_PARTIAL)
{
state = f.ViewVolumeTest(mCenter, GetRadius());
#if DEMO
if (state != VS_OUTSIDE)
{
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
}
#endif
}
if (HasSpherePackFlag(SPF_SUPERSPHERE))
{
if (state == VS_OUTSIDE)
{
if (HasSpherePackFlag(SPF_HIDDEN)) return; // no state change
ClearSpherePackFlag(SpherePackFlag(SPF_INSIDE | SPF_PARTIAL));
SetSpherePackFlag(SPF_HIDDEN);
}
else
{
if (state == VS_INSIDE)
{
if (HasSpherePackFlag(SPF_INSIDE)) return; // no state change
ClearSpherePackFlag(SpherePackFlag(SPF_PARTIAL | SPF_HIDDEN));
SetSpherePackFlag(SPF_INSIDE);
}
else
{
ClearSpherePackFlag(SpherePackFlag(SPF_HIDDEN | SPF_INSIDE));
SetSpherePackFlag(SPF_PARTIAL);
}
}
SpherePack *pack = mChildren;
while (pack)
{
pack->VisibilityTest(f,callback,state);
pack = pack->_GetNextSibling();
}
}
else
{
switch (state)
{
case VS_INSIDE:
if (!HasSpherePackFlag(SPF_INSIDE))
{
ClearSpherePackFlag(SpherePackFlag(SPF_HIDDEN | SPF_PARTIAL));
SetSpherePackFlag(SPF_INSIDE);
callback->VisibilityCallback(f,this,state);
}
break;
case VS_OUTSIDE:
if (!HasSpherePackFlag(SPF_HIDDEN))
{
ClearSpherePackFlag(SpherePackFlag(SPF_INSIDE | SPF_PARTIAL));
SetSpherePackFlag(SPF_HIDDEN);
callback->VisibilityCallback(f,this,state);
}
break;
case VS_PARTIAL:
//if (!HasSpherePackFlag(SPF_PARTIAL))
{
ClearSpherePackFlag(SpherePackFlag(SPF_INSIDE | SPF_HIDDEN));
SetSpherePackFlag(SPF_PARTIAL);
callback->VisibilityCallback(f,this,state);
}
break;
}
}
}
void SpherePackFactory::RayTrace(const Vector3d &p1,
const Vector3d &p2,
SpherePackCallback *callback)
{
// test case here, just traverse children.
Vector3d dir = p2;
float dist = dir.Normalize();
mCallback = callback;
mRoot->RayTrace(p1,dir,dist,this);
}
#include "../EterBase/Debug.h"
void SpherePackFactory::RangeTest(const Vector3d &center,float radius,SpherePackCallback *callback)
{
#ifdef __STATIC_RANGE__
if (!center.IsInStaticRange())
{
TraceError("SpherePackFactory::RangeTest - RANGE ERROR %f, %f, %f",
center.x, center.y, center.z);
assert("SpherePackFactory::RangeTest - RANGE ERROR");
return;
}
#endif
mCallback = callback;
mRoot->RangeTest(center,radius,this,VS_PARTIAL);
}
void SpherePackFactory::PointTest2d(const Vector3d &center, SpherePackCallback *callback)
{
#ifdef __STATIC_RANGE__
if (!center.IsInStaticRange())
{
TraceError("SpherePackFactory::RangeTest2d - RANGE ERROR %f, %f, %f",
center.x, center.y, center.z);
assert("SpherePackFactory::RangeTest2d - RANGE ERROR");
return;
}
#endif
mCallback = callback;
#ifdef SPHERELIB_STRICT
mRoot->PointTest2d(center, this,VS_PARTIAL);
extern bool MAPOUTDOOR_GET_HEIGHT_TRACE;
if (MAPOUTDOOR_GET_HEIGHT_TRACE)
puts("================================================");
#else
mRoot->PointTest2d(center, this,VS_PARTIAL);
#endif
}
void SpherePack::RangeTest(const Vector3d &p,
float distance,
SpherePackCallback *callback,
ViewState state)
{
if (state == VS_PARTIAL)
{
float d = p.Distance(mCenter);
if ((d-distance) > GetRadius()) return;;
if ((GetRadius()+d) < distance) state = VS_INSIDE;
}
if (HasSpherePackFlag(SPF_SUPERSPHERE))
{
#if DEMO
if (state == VS_PARTIAL)
{
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
}
#endif
SpherePack *pack = mChildren;
while (pack)
{
pack->RangeTest(p,distance,callback,state);
pack = pack->_GetNextSibling();
}
}
else
{
callback->RangeTestCallback(p,distance,this,state);
}
}
void SpherePack::PointTest2d(const Vector3d &p,
SpherePackCallback *callback,
ViewState state)
{
if (state == VS_PARTIAL)
{
float dx=p.x-mCenter.x;
float dy=p.y-mCenter.y;
float distSquare = (dx*dx)+(dy*dy);
if (distSquare > GetRadius2()) return;;
if (GetRadius2() < -distSquare) state = VS_INSIDE;
}
if (HasSpherePackFlag(SPF_SUPERSPHERE))
{
#if DEMO
if (state == VS_PARTIAL)
{
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
}
#endif
SpherePack *pack = mChildren;
while (pack)
{
pack->PointTest2d(p, callback, state);
pack = pack->_GetNextSibling();
}
}
else
{
#ifdef SPHERELIB_STRICT
extern bool MAPOUTDOOR_GET_HEIGHT_TRACE;
if (MAPOUTDOOR_GET_HEIGHT_TRACE)
{
float dx=p.x-mCenter.x;
float dy=p.y-mCenter.y;
float distSquare = (dx*dx)+(dy*dy);
printf("--- (%f, %f) dist %f radius %f isSphere %d\n", mCenter.x, mCenter.y, distSquare, GetRadius(), IS_SPHERE);
}
#endif
callback->PointTest2dCallback(p, this, state);
}
}
void SpherePackFactory::RangeTestCallback(const Vector3d &p,float distance,SpherePack *sphere,ViewState state)
{
#ifdef SPHERELIB_STRICT
if (!sphere->IS_SPHERE)
puts("SpherePackFactory::RangeTestCallback");
#endif
SpherePack *link = (SpherePack *) sphere->GetUserData();
if (link) link->RangeTest(p,distance,mCallback,state);
};
void SpherePackFactory::PointTest2dCallback(const Vector3d &p, SpherePack *sphere,ViewState state)
{
#ifdef SPHERELIB_STRICT
if (!sphere->IS_SPHERE)
puts("SpherePackFactory::PointTest2dCallback");
#endif
SpherePack *link = (SpherePack *) sphere->GetUserData();
if (link) link->PointTest2d(p, mCallback,state);
};
void SpherePack::RayTrace(const Vector3d &p1,
const Vector3d &dir,
float distance,
SpherePackCallback *callback)
{
bool hit = false;
if (HasSpherePackFlag(SPF_SUPERSPHERE))
{
hit = RayIntersectionInFront(p1,dir,0);
if (hit)
{
#if DEMO
DrawCircle(int(mCenter.x), int(mCenter.y), int(GetRadius()), 0x404040);
#endif
SpherePack *pack = mChildren;
while (pack)
{
pack->RayTrace(p1,dir,distance,callback);
pack = pack->_GetNextSibling();
}
}
}
else
{
Vector3d sect;
hit = RayIntersection(p1,dir,distance,&sect);
if (hit)
{
callback->RayTraceCallback(p1,dir,distance,sect,this);
}
}
}
void SpherePackFactory::RayTraceCallback(const Vector3d &p1, // source pos of ray
const Vector3d &dir, // direction of ray
float distance, // distance of ray
const Vector3d &/*sect*/, // intersection location
SpherePack *sphere)
{
#ifdef SPHERELIB_STRICT
if (!sphere->IS_SPHERE)
puts("SpherePackFactory::RayTraceCallback");
#endif
SpherePack *link = (SpherePack *) sphere->GetUserData();
if (link) link->RayTrace(p1,dir,distance,mCallback);
};
void SpherePackFactory::Reset(void)
{
mRoot->Reset();
mLeaf->Reset();
}
void SpherePack::Reset(void)
{
ClearSpherePackFlag(SpherePackFlag(SPF_HIDDEN | SPF_PARTIAL | SPF_INSIDE));
SpherePack *pack = mChildren;
while (pack)
{
pack->Reset();
pack = pack->_GetNextSibling();
}
}
void SpherePackFactory::VisibilityCallback(const Frustum &f,SpherePack *sphere,ViewState state)
{
#ifdef SPHERELIB_STRICT
if (!sphere->IS_SPHERE)
puts("SpherePackFactory::VisibilityCallback");
#endif
SpherePack *link = (SpherePack *) sphere->GetUserData();
if (link) link->VisibilityTest(f,mCallback,state);
}
@@ -2775,6 +2775,9 @@ void CInstanceBase::ChangeWeapon(DWORD eWeapon)
if (SetWeapon(eWeapon))
RefreshState(CRaceMotionData::NAME_WAIT, true);
if (IsAffect(AFFECT_GEOMGYEONG))
__Warrior_SetGeomgyeongAffect(true);
}
bool CInstanceBase::ChangeArmor(DWORD dwArmor)
@@ -3028,6 +3031,7 @@ void CInstanceBase::__Warrior_Initialize()
void CInstanceBase::__Initialize()
{
__Warrior_Initialize();
memset(m_byAffectGrade, 0, sizeof(m_byAffectGrade));
__StoneSmoke_Inialize();
__EffectContainer_Initialize();
__InitializeRotationSpeed();
@@ -436,6 +436,7 @@ class CInstanceBase
// 스크립트용 테스트 함수. 나중에 없에자
void SCRIPT_SetAffect(UINT eAffect, bool isVisible);
void __Warrior_SetGeomgyeongAffect(bool isVisible);
float CalculateDistanceSq3d(const TPixelPosition& c_rkPPosDst);
@@ -803,7 +804,7 @@ class CInstanceBase
void __ClearMainInstance();
void __Shaman_SetParalysis(bool isParalysis);
void __Warrior_SetGeomgyeongAffect(bool isVisible);
BYTE __GetAffectGrade(UINT eAffect);
void __Assassin_SetEunhyeongAffect(bool isVisible);
void __SetReviveInvisibilityAffect(bool isVisible);
@@ -1045,6 +1046,7 @@ class CInstanceBase
};
SWarrior m_kWarrior;
BYTE m_byAffectGrade[AFFECT_NUM];
void __Warrior_Initialize();
@@ -340,6 +340,66 @@ bool CInstanceBase::NEW_UseSkill(UINT uSkill, UINT uMot, UINT uMotLoopCount, boo
m_GraphicThingInstance.__OnUseSkill(uMot, uMotLoopCount, isMovingSkill);
BYTE bGrade = (uMot >= 25) ? (uMot / 25) : 0;
if (bGrade > 3)
bGrade = 3;
if (uSkill != 0)
{
switch (uSkill)
{
case 3: m_byAffectGrade[AFFECT_JEONGWI] = bGrade; break;
case 4: m_byAffectGrade[AFFECT_GEOMGYEONG] = bGrade; break;
case 19: m_byAffectGrade[AFFECT_CHEONGEUN] = bGrade; break;
case 34: m_byAffectGrade[AFFECT_EUNHYEONG] = bGrade; break;
case 49: m_byAffectGrade[AFFECT_GYEONGGONG] = bGrade; break;
case 63: m_byAffectGrade[AFFECT_GWIGEOM] = bGrade; break;
case 64: m_byAffectGrade[AFFECT_GONGPO] = bGrade; break;
case 65: m_byAffectGrade[AFFECT_JUMAGAP] = bGrade; break;
case 78: m_byAffectGrade[AFFECT_MUYEONG] = bGrade; break;
case 79: m_byAffectGrade[AFFECT_HEUKSIN] = bGrade; break;
case 94: m_byAffectGrade[AFFECT_HOSIN] = bGrade; break;
case 95: m_byAffectGrade[AFFECT_BOHO] = bGrade; break;
case 96: m_byAffectGrade[AFFECT_GICHEON] = bGrade; break;
case 110: m_byAffectGrade[AFFECT_KWAESOK] = bGrade; break;
case 111: m_byAffectGrade[AFFECT_JEUNGRYEOK] = bGrade; break;
}
}
else
{
UINT uMotSub = uMot % 25;
int iJob = RaceToJob(GetRace());
switch (iJob)
{
case NRaceData::JOB_WARRIOR:
if (uMotSub == 3) m_byAffectGrade[AFFECT_JEONGWI] = bGrade;
else if (uMotSub == 4) m_byAffectGrade[AFFECT_GEOMGYEONG] = bGrade;
else if (uMotSub == 19) m_byAffectGrade[AFFECT_CHEONGEUN] = bGrade;
break;
case NRaceData::JOB_ASSASSIN:
if (uMotSub == 4) m_byAffectGrade[AFFECT_EUNHYEONG] = bGrade;
else if (uMotSub == 19) m_byAffectGrade[AFFECT_GYEONGGONG] = bGrade;
break;
case NRaceData::JOB_SURA:
if (uMotSub == 3) m_byAffectGrade[AFFECT_GWIGEOM] = bGrade;
else if (uMotSub == 4) m_byAffectGrade[AFFECT_GONGPO] = bGrade;
else if (uMotSub == 5) m_byAffectGrade[AFFECT_JUMAGAP] = bGrade;
else if (uMotSub == 18) m_byAffectGrade[AFFECT_MUYEONG] = bGrade;
else if (uMotSub == 19) m_byAffectGrade[AFFECT_HEUKSIN] = bGrade;
break;
case NRaceData::JOB_SHAMAN:
if (uMotSub == 4) m_byAffectGrade[AFFECT_HOSIN] = bGrade;
else if (uMotSub == 5) m_byAffectGrade[AFFECT_BOHO] = bGrade;
else if (uMotSub == 6) m_byAffectGrade[AFFECT_GICHEON] = bGrade;
else if (uMotSub == 20) m_byAffectGrade[AFFECT_KWAESOK] = bGrade;
else if (uMotSub == 21) m_byAffectGrade[AFFECT_JEUNGRYEOK] = bGrade;
break;
}
}
if (IsAffect(AFFECT_GEOMGYEONG))
__Warrior_SetGeomgyeongAffect(true);
if (uMotLoopCount > 0)
m_GraphicThingInstance.SetMotionLoopCount(uMotLoopCount);
@@ -825,6 +825,29 @@ void CInstanceBase::__Shaman_SetParalysis(bool isParalysis)
BYTE CInstanceBase::__GetAffectGrade(UINT eAffect)
{
if (__IsMainInstance())
{
DWORD dwSkillIndex = 0;
if (CPythonPlayer::Instance().AffectIndexToSkillIndex(eAffect, &dwSkillIndex))
{
DWORD dwSkillSlotIndex = 0;
if (CPythonPlayer::Instance().GetSkillSlotIndex(dwSkillIndex, &dwSkillSlotIndex))
{
int iGrade = CPythonPlayer::Instance().GetSkillGrade(dwSkillSlotIndex);
if (iGrade >= 0 && iGrade < 4)
return (BYTE)iGrade;
}
}
}
if (eAffect < AFFECT_NUM)
return m_byAffectGrade[eAffect];
return 0;
}
void CInstanceBase::__Warrior_SetGeomgyeongAffect(bool isVisible)
{
if (isVisible)
@@ -836,10 +859,38 @@ void CInstanceBase::__Warrior_SetGeomgyeongAffect(bool isVisible)
__DetachEffect(m_kWarrior.m_dwGeomgyeongEffect);
m_GraphicThingInstance.SetReachScale(1.5f);
if (m_GraphicThingInstance.IsTwoHandMode())
m_kWarrior.m_dwGeomgyeongEffect=__AttachEffect(EFFECT_WEAPON+WEAPON_TWOHAND);
BYTE bGrade = __GetAffectGrade(AFFECT_GEOMGYEONG);
if (bGrade >= 4)
bGrade = 3;
static const char * c_szGeomSpearFiles[4] = {
"d:/ymir work/pc/warrior/effect/geom_spear_loop.mse",
"d:/ymir work/pc/warrior/effect/geom_2_spear_loop.mse",
"d:/ymir work/pc/warrior/effect/geom_3_spear_loop.mse",
"d:/ymir work/pc/warrior/effect/geom_4_spear_loop.mse"
};
static const char * c_szGeomSwordFiles[4] = {
"d:/ymir work/pc/warrior/effect/geom_sword_loop.mse",
"d:/ymir work/pc/warrior/effect/geom_2_sword_loop.mse",
"d:/ymir work/pc/warrior/effect/geom_3_sword_loop.mse",
"d:/ymir work/pc/warrior/effect/geom_4_sword_loop.mse"
};
const char * c_szFileName = m_GraphicThingInstance.IsTwoHandMode() ? c_szGeomSpearFiles[bGrade] : c_szGeomSwordFiles[bGrade];
DWORD dwEffectCRC = 0;
if (CEffectManager::Instance().RegisterEffect2(c_szFileName, &dwEffectCRC, true))
{
m_kWarrior.m_dwGeomgyeongEffect = m_GraphicThingInstance.AttachEffectByID(0, "equip_right_hand", dwEffectCRC);
}
else
m_kWarrior.m_dwGeomgyeongEffect=__AttachEffect(EFFECT_WEAPON+WEAPON_ONEHAND);
{
if (m_GraphicThingInstance.IsTwoHandMode())
m_kWarrior.m_dwGeomgyeongEffect = __AttachEffect(EFFECT_WEAPON + WEAPON_TWOHAND);
else
m_kWarrior.m_dwGeomgyeongEffect = __AttachEffect(EFFECT_WEAPON + WEAPON_ONEHAND);
}
}
else
{
@@ -1033,6 +1084,129 @@ DWORD CInstanceBase::__AttachEffect(UINT eEftType)
if (eEftType>=EFFECT_NUM)
return 0;
if (eEftType >= EFFECT_AFFECT && eEftType < EFFECT_AFFECT_END)
{
UINT eAffect = eEftType - EFFECT_AFFECT;
BYTE bGrade = __GetAffectGrade(eAffect);
if (bGrade >= 4)
bGrade = 3;
const char * c_szFileName = NULL;
const char * c_szBoneName = NULL;
switch (eAffect)
{
case AFFECT_GWIGEOM:
{
static const char * c_szGwigeomFiles[4] = {
"d:/ymir work/pc/sura/effect/gwigeom_loop.mse",
"d:/ymir work/pc/sura/effect/gwigeom_2_loop.mse",
"d:/ymir work/pc/sura/effect/gwigeom_3_loop.mse",
"d:/ymir work/pc/sura/effect/gwigeom_4_loop.mse"
};
c_szFileName = c_szGwigeomFiles[bGrade];
c_szBoneName = "Bip01 R Finger2";
break;
}
case AFFECT_GONGPO:
{
static const char * c_szFearFiles[4] = {
"d:/ymir work/pc/sura/effect/fear_loop.mse",
"d:/ymir work/pc/sura/effect/fear_2_loop.mse",
"d:/ymir work/pc/sura/effect/fear_3_loop.mse",
"d:/ymir work/pc/sura/effect/fear_3_loop.mse"
};
c_szFileName = c_szFearFiles[bGrade];
c_szBoneName = "";
break;
}
case AFFECT_JUMAGAP:
{
static const char * c_szJumagapFiles[4] = {
"d:/ymir work/pc/sura/effect/jumagap_loop.mse",
"d:/ymir work/pc/sura/effect/jumagap_2_loop.mse",
"d:/ymir work/pc/sura/effect/jumagap_3_loop.mse",
"d:/ymir work/pc/sura/effect/jumagap_4_loop.mse"
};
c_szFileName = c_szJumagapFiles[bGrade];
c_szBoneName = "";
break;
}
case AFFECT_HEUKSIN:
{
static const char * c_szHeuksinFiles[4] = {
"d:/ymir work/pc/sura/effect/heuksin_loop.mse",
"d:/ymir work/pc/sura/effect/heuksin_2_loop.mse",
"d:/ymir work/pc/sura/effect/heuksin_3_loop.mse",
"d:/ymir work/pc/sura/effect/heuksin_4_loop.mse"
};
c_szFileName = c_szHeuksinFiles[bGrade];
c_szBoneName = "";
break;
}
case AFFECT_HOSIN:
{
c_szFileName = (bGrade >= 3) ? "d:/ymir work/pc/shaman/effect/3hosin_loop_4.mse" : "d:/ymir work/pc/shaman/effect/3hosin_loop.mse";
c_szBoneName = "";
break;
}
case AFFECT_BOHO:
{
c_szFileName = (bGrade >= 3) ? "d:/ymir work/pc/shaman/effect/boho_loop_4.mse" : "d:/ymir work/pc/shaman/effect/boho_loop.mse";
c_szBoneName = "";
break;
}
case AFFECT_GYEONGGONG:
{
static const char * c_szGyeonggongFiles[4] = {
"d:/ymir work/pc/assassin/effect/gyeonggong_loop.mse",
"d:/ymir work/pc/assassin/effect/gyeonggong_2_loop.mse",
"d:/ymir work/pc/assassin/effect/gyeonggong_3_loop.mse",
"d:/ymir work/pc/assassin/effect/gyeonggong_4_loop.mse"
};
c_szFileName = c_szGyeonggongFiles[bGrade];
c_szBoneName = "";
break;
}
case AFFECT_CHEONGEUN:
{
static const char * c_szCheongeunFiles[4] = {
"d:/ymir work/pc/warrior/effect/gyeokgongjang_loop.mse",
"d:/ymir work/pc/warrior/effect/gyeokgongjang_2_loop.mse",
"d:/ymir work/pc/warrior/effect/gyeokgongjang_3_loop.mse",
"d:/ymir work/pc/warrior/effect/gyeokgongjang_3_loop.mse"
};
c_szFileName = c_szCheongeunFiles[bGrade];
c_szBoneName = "";
break;
}
case AFFECT_GICHEON:
{
c_szFileName = (bGrade >= 3) ? "d:/ymir work/pc/shaman/effect/6gicheon_hand_4.mse" : "d:/ymir work/pc/shaman/effect/6gicheon_hand.mse";
c_szBoneName = "Bip01 R Hand";
break;
}
case AFFECT_JEUNGRYEOK:
{
c_szFileName = (bGrade >= 3) ? "d:/ymir work/pc/shaman/effect/jeungryeok_hand_4.mse" : "d:/ymir work/pc/shaman/effect/jeungryeok_hand.mse";
c_szBoneName = "Bip01 L Hand";
break;
}
default:
break;
}
if (c_szFileName)
{
DWORD dwEffectCRC = 0;
if (CEffectManager::Instance().RegisterEffect2(c_szFileName, &dwEffectCRC, true))
{
const char * bone = (c_szBoneName && c_szBoneName[0]) ? c_szBoneName : NULL;
return m_GraphicThingInstance.AttachEffectByID(0, bone, dwEffectCRC);
}
}
}
if (ms_astAffectEffectAttachBone[eEftType].empty())
{
return m_GraphicThingInstance.AttachEffectByID(0, NULL, ms_adwCRCAffectEffect[eEftType]);
@@ -274,17 +274,7 @@ class CPythonApplication final : public CMSApplication, public CInputKeyboard, p
int m_nLeft, m_nRight, m_nTop, m_nBottom;
protected:
LRESULT WindowProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
void OnCameraUpdate();
void OnUIUpdate();
void OnUIRender();
void OnMouseUpdate();
void OnMouseRender();
public:
void OnMouseWheel(int nLen);
void OnMouseMove(int x, int y);
void OnMouseMiddleButtonDown(int x, int y);
@@ -299,6 +289,17 @@ class CPythonApplication final : public CMSApplication, public CInputKeyboard, p
void OnKeyUp(int iIndex);
void OnIMEKeyDown(int iIndex);
protected:
LRESULT WindowProcedure(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
void OnCameraUpdate();
void OnUIUpdate();
void OnUIRender();
void OnMouseUpdate();
void OnMouseRender();
int CheckDeviceState();
BOOL __IsContinuousChangeTypeCursor(int iCursorNum);
@@ -254,10 +254,34 @@ void CPythonBackground::__CreateProperty()
{
// The exported client runs with player settings as CWD; its read-only property pack
// lives beside the other bundled 40250 packs. Use the same root registered by PackBackend.
std::string property_pack = "pack/property";
std::vector<std::string> candidates;
if (const char* client = getenv("MT_40250_CLIENT"))
property_pack = std::string(client) + "/pack/Property";
m_PropertyManager.Initialize(property_pack.c_str());
{
candidates.push_back(std::string(client) + "/pack/Property");
candidates.push_back(std::string(client) + "/pack/property");
}
candidates.push_back("pack/Property");
candidates.push_back("pack/property");
candidates.push_back("Client/pack/Property");
candidates.push_back("Client/pack/property");
bool initialized = false;
for (const auto& path : candidates)
{
std::string eix = path + ".eix";
if (_access(eix.c_str(), 0) == 0)
{
if (m_PropertyManager.Initialize(path.c_str()))
{
initialized = true;
break;
}
}
}
if (!initialized && !candidates.empty())
{
m_PropertyManager.Initialize(candidates.front().c_str());
}
}
}
@@ -735,6 +735,11 @@ void CPythonCharacterManager::__SortPickedActorList()
std::sort(m_kVct_pkInstPicked.begin(), m_kVct_pkInstPicked.end(), kLess);
}
void CPythonCharacterManager::Pick()
{
__NEW_Pick();
}
void CPythonCharacterManager::__NEW_Pick()
{
__UpdateSortPickedActorList();
@@ -773,31 +778,10 @@ void CPythonCharacterManager::__NEW_Pick()
}
}
// 못찾겠으면 걍 순서대로
{
std::vector<CInstanceBase*>::iterator f;
for (f=m_kVct_pkInstPicked.begin(); f!=m_kVct_pkInstPicked.end(); ++f)
{
CInstanceBase* pkInstEach=*f;
if (pkInstEach!=pkInstMain)
{
if (m_pkInstPick)
if (m_pkInstPick!=pkInstEach)
m_pkInstPick->OnUnselected();
if (pkInstEach->CanPickInstance())
{
m_pkInstPick = pkInstEach;
m_pkInstPick->OnSelected();
return;
}
}
}
}
if (pkInstMain)
if (pkInstMain->CanPickInstance())
if (m_kVct_pkInstPicked.end() != std::find(m_kVct_pkInstPicked.begin(), m_kVct_pkInstPicked.end(), pkInstMain))
if (pkInstMain->IntersectBoundingBox())
{
if (m_pkInstPick)
if (m_pkInstPick!=pkInstMain)
@@ -85,6 +85,7 @@ class CPythonCharacterManager : public CSingleton<CPythonCharacterManager>, publ
CInstanceBase * GetInstancePtrByName(const char *name);
// Pick
void Pick();
int PickAll();
CInstanceBase * GetCloseInstance(CInstanceBase * pInstance);
@@ -1037,6 +1037,25 @@ void CPythonPlayer::SetSkillLevel_(DWORD dwSkillIndex, DWORD dwSkillGrade, DWORD
m_playerStatus.aSkill[dwSlotIndex].fcurEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel)/100.0f;
m_playerStatus.aSkill[dwSlotIndex].fnextEfficientPercentage = LocaleService_GetSkillPower(dwSkillLevel+1)/100.0f;
CInstanceBase * pkInstMain = NEW_GetMainActorPtr();
if (pkInstMain)
{
if (dwSkillIndex == 4 && pkInstMain->IsAffect(CInstanceBase::AFFECT_GEOMGYEONG))
{
pkInstMain->__Warrior_SetGeomgyeongAffect(true);
}
else
{
for (std::map<DWORD, DWORD>::const_iterator it = m_kMap_dwAffectIndexToSkillIndex.begin(); it != m_kMap_dwAffectIndexToSkillIndex.end(); ++it)
{
if (it->second == dwSkillIndex && pkInstMain->IsAffect(it->first))
{
pkInstMain->SCRIPT_SetAffect(it->first, false);
pkInstMain->SCRIPT_SetAffect(it->first, true);
}
}
}
}
}
void CPythonPlayer::SetSkillCoolTime(DWORD dwSkillIndex)
@@ -12,7 +12,7 @@ std::map<std::string, DWORD> CPythonSkill::SSkillData::ms_NewMinStatusNameMap;
std::map<std::string, DWORD> CPythonSkill::SSkillData::ms_NewMaxStatusNameMap;
DWORD CPythonSkill::SSkillData::ms_dwTimeIncreaseSkillNumber = 0;
BOOL SKILL_EFFECT_UPGRADE_ENABLE = FALSE;
BOOL SKILL_EFFECT_UPGRADE_ENABLE = TRUE;
int SplitLine(const char * c_szText, CTokenVector* pstTokenVector, const char * c_szDelimeter)
{
+1
View File
@@ -757,6 +757,7 @@ struct IDirect3DDevice8
virtual ULONG Release() = 0;
virtual HRESULT GetDeviceCaps(D3DCAPS8* pCaps) = 0;
virtual HRESULT GetViewport(D3DVIEWPORT8* pViewport) = 0;
virtual HRESULT SetViewport(const D3DVIEWPORT8* pViewport) = 0;
virtual UINT GetAvailableTextureMem() = 0;
virtual HRESULT BeginScene() = 0;
virtual HRESULT EndScene() = 0;