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
+27
View File
@@ -369,6 +369,9 @@ const Metin2World::Chunk *Metin2World::chunk_at(int tx, int ty) const {
}
bool Metin2World::build_chunk(int tx, int ty) {
const bool profile_map = std::getenv("MT_PROFILE_MAP") != nullptr;
const auto ticks = [] { return Time::get_singleton()->get_ticks_usec(); };
const double profile_start = profile_map ? ticks() : 0;
const std::string dir =
std::string(map_dir().utf8().get_data()) + "/" + fmt::m2coord::tile_dir(tx, ty);
auto hm = std::make_shared<fmt::HeightMap>();
@@ -378,12 +381,15 @@ bool Metin2World::build_chunk(int tx, int ty) {
++chunks_failed;
return false;
}
const double profile_height = profile_map ? ticks() : 0;
auto am = std::make_shared<fmt::AttrMap>();
if (!fmt::load_attr_map(dir + "/attr.atr", *am, &err))
am.reset(); // 非致命:attr 缺失 -> 该区块无阻挡
const double profile_attr = profile_map ? ticks() : 0;
fmt::TerrainMesh tmesh;
fmt::build_terrain_mesh(*hm, tx, ty, setting.height_scale, tmesh);
const double profile_terrain = profile_map ? ticks() : 0;
Ref<Material> mat;
{
@@ -394,19 +400,26 @@ bool Metin2World::build_chunk(int tx, int ty) {
}
bool splatted = false;
double profile_tile_ms = 0, profile_alpha_ms = 0, profile_material_ms = 0;
if (splat_ready && resolver) {
fmt::TileMap tile;
std::string e2;
const double tile_start = profile_map ? ticks() : 0;
if (fmt::load_tile_map(dir + "/tile.raw", tile, &e2)) {
if (profile_map) profile_tile_ms = (ticks() - tile_start) / 1000.0;
fmt::SplatSet ss;
const double alpha_start = profile_map ? ticks() : 0;
fmt::build_splat(tile, texture_set.runtime_count(), ss);
if (profile_map) profile_alpha_ms = (ticks() - alpha_start) / 1000.0;
if (!ss.layers.empty()) {
String smpath;
String sm = String(dir.c_str()) + "/shadowmap.dds";
if (mtgodot::file_exists(sm))
smpath = sm;
const double material_start = profile_map ? ticks() : 0;
Ref<ShaderMaterial> tm = build_chunk_terrain_material(
ss, texture_set, *resolver, smpath);
if (profile_map) profile_material_ms = (ticks() - material_start) / 1000.0;
if (tm.is_valid()) {
mat = tm;
splatted = true;
@@ -416,6 +429,7 @@ bool Metin2World::build_chunk(int tx, int ty) {
}
if (splatted)
++chunks_splatted;
const double profile_splat = profile_map ? ticks() : 0;
// 该区块的场景根 —— terrain / water / 对象 / 树都挂它下面,卸载 = free 它
Node3D *croot = memnew(Node3D);
@@ -483,6 +497,7 @@ bool Metin2World::build_chunk(int tx, int ty) {
}
}
++chunks_built;
const double profile_godot_mesh = profile_map ? ticks() : 0;
// 地形碰撞(W1 item 6):HeightMapShape3D,129×129,格距 = CELL_M(缩放承载)
if (collision_enabled) {
@@ -545,6 +560,18 @@ bool Metin2World::build_chunk(int tx, int ty) {
objects_placed += ck.objects;
trees_placed += ck.trees;
chunks.push_back(std::move(ck));
if (profile_map) {
const double profile_end = ticks();
UtilityFunctions::print(vformat("MAP_PROFILE %d,%d height=%.3f attr=%.3f terrain_cpu=%.3f splat=%.3f tile=%.3f alpha=%.3f material=%.3f godot_mesh=%.3f rest=%.3f total=%.3f",
tx, ty, (profile_height - profile_start) / 1000.0,
(profile_attr - profile_height) / 1000.0,
(profile_terrain - profile_attr) / 1000.0,
(profile_splat - profile_terrain) / 1000.0,
profile_tile_ms, profile_alpha_ms, profile_material_ms,
(profile_godot_mesh - profile_splat) / 1000.0,
(profile_end - profile_godot_mesh) / 1000.0,
(profile_end - profile_start) / 1000.0));
}
return true;
}
@@ -13,15 +13,21 @@
#include <gr2/gr2.h>
#include "granny.h"
#include "../EterLib/RenderCommands3D.h"
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
namespace
{
using Mat4 = gr2::Mat4;
@@ -815,16 +821,179 @@ int read_influences(const Layout& l, int m, const uint8_t* v, float* out, bool w
}
return n;
}
struct StaticSkinMesh
{
std::uint64_t source_mesh_key = 0;
std::uint32_t vertex_count = 0;
std::uint32_t out_stride = 0;
std::uint32_t bone_count = 1;
std::vector<std::uint8_t> bind_vertices;
std::vector<std::uint8_t> bone_indices; // vertex_count * 4
std::vector<float> bone_weights; // vertex_count * 4
};
struct DestSkinSlice
{
const std::uint8_t* dest_begin = nullptr;
const std::uint8_t* dest_end = nullptr;
std::uint32_t stride = 0;
std::uint32_t vertex_count = 0;
const StaticSkinMesh* source = nullptr;
std::vector<float> bone_matrices; // bone_count * 16
};
std::mutex g_skin_mutex;
int g_gpu_skinning_override = -1;
std::unordered_map<const void*, std::unique_ptr<StaticSkinMesh>> g_static_skin_meshes;
std::map<const std::uint8_t*, DestSkinSlice> g_dest_skin_slices;
} // namespace
void SetGpuSkinningEnabled(bool enabled)
{
std::lock_guard<std::mutex> lock(g_skin_mutex);
g_gpu_skinning_override = enabled ? 1 : 0;
if (!enabled)
g_dest_skin_slices.clear();
}
bool IsGpuSkinningEnabled()
{
if (g_gpu_skinning_override >= 0)
return g_gpu_skinning_override != 0;
static const bool env_on = [] {
const char* v = std::getenv("MT_GPU_SKINNING");
return v && (*v == '1' || *v == 't' || *v == 'T' || *v == 'y' || *v == 'Y');
}();
return env_on;
}
bool LookupGpuSkinSubrange(const void* vertex_buffer_base, std::uint32_t stride,
std::uint32_t lo_vertex, std::uint32_t hi_vertex,
GpuSkinSubrangeView* out_view)
{
if (!IsGpuSkinningEnabled() || !vertex_buffer_base || !stride || !out_view || hi_vertex < lo_vertex)
return false;
std::lock_guard<std::mutex> lock(g_skin_mutex);
const auto* base = static_cast<const std::uint8_t*>(vertex_buffer_base);
const auto* q_begin = base + std::size_t(lo_vertex) * stride;
const auto* q_end = base + std::size_t(hi_vertex + 1) * stride;
auto it = g_dest_skin_slices.upper_bound(q_begin);
if (it == g_dest_skin_slices.begin())
return false;
--it;
const DestSkinSlice& slice = it->second;
if (!slice.source || slice.stride != stride || q_begin < slice.dest_begin || q_end > slice.dest_end || slice.dest_begin < base)
return false;
const std::size_t byte_offset = static_cast<std::size_t>(slice.dest_begin - base);
if (byte_offset % stride != 0)
return false;
out_view->source_mesh_key = slice.source->source_mesh_key;
out_view->mesh_base_vertex = static_cast<std::uint32_t>(byte_offset / stride);
out_view->mesh_vertex_count = slice.vertex_count;
out_view->bone_indices = slice.source->bone_indices.data();
out_view->bone_weights = slice.source->bone_weights.data();
out_view->bone_matrices = slice.bone_matrices.data();
out_view->bone_count = slice.source->bone_count;
return true;
}
void GrannyDeformVertices(granny_mesh_deformer const* Deformer, granny_int32x const* MatrixIndices,
granny_real32 const* MatrixBuffer4x4, granny_int32x VertexCount,
void const* SourceVertices, void* DestVertices)
{
if (!Deformer || !MatrixBuffer4x4 || !SourceVertices || !DestVertices)
if (!Deformer || !MatrixBuffer4x4 || !SourceVertices || !DestVertices || VertexCount <= 0)
return;
const granny_mesh_deformer& d = *Deformer;
const bool normals = d.type != GrannyDeformPosition && d.in_normal >= 0 && d.out_normal >= 0;
if (IsGpuSkinningEnabled())
{
std::lock_guard<std::mutex> lock(g_skin_mutex);
auto& mesh_ptr = g_static_skin_meshes[SourceVertices];
if (!mesh_ptr || mesh_ptr->vertex_count != std::uint32_t(VertexCount) || mesh_ptr->out_stride != std::uint32_t(d.out.size))
{
mesh_ptr = std::make_unique<StaticSkinMesh>();
mesh_ptr->source_mesh_key = static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(SourceVertices));
mesh_ptr->vertex_count = static_cast<std::uint32_t>(VertexCount);
mesh_ptr->out_stride = static_cast<std::uint32_t>(d.out.size);
mesh_ptr->bind_vertices.resize(std::size_t(VertexCount) * d.out.size, 0);
mesh_ptr->bone_indices.resize(std::size_t(VertexCount) * 4, 0);
mesh_ptr->bone_weights.resize(std::size_t(VertexCount) * 4, 0.0f);
int max_local_bone = 0;
for (int v = 0; v < VertexCount; ++v)
{
const uint8_t* src = static_cast<const uint8_t*>(SourceVertices) + std::size_t(v) * d.in.size;
uint8_t* dst = mesh_ptr->bind_vertices.data() + std::size_t(v) * d.out.size;
run_conversion(d.tail, src, dst);
float p[3] = {0, 0, 0}, nrm[3] = {0, 0, 0};
read_vec(d.in, d.in_position, src, p, 3);
write_vec(d.out, d.out_position, dst, p, 3);
if (normals)
{
read_vec(d.in, d.in_normal, src, nrm, 3);
write_vec(d.out, d.out_normal, dst, nrm, 3);
}
float weights[4] = {1, 0, 0, 0}, indices[4] = {0, 0, 0, 0};
int wn = 1, in = 1;
if (d.in_weights >= 0)
wn = read_influences(d.in, d.in_weights, src, weights, true);
if (d.in_indices >= 0)
in = read_influences(d.in, d.in_indices, src, indices, false);
const int n = std::min(wn, in);
for (int k = 0; k < n; ++k)
{
const float w = weights[k];
if (w <= 0.0f)
continue;
const int local = std::max(0, std::min(127, int(indices[k])));
mesh_ptr->bone_indices[std::size_t(v) * 4 + k] = static_cast<std::uint8_t>(local);
mesh_ptr->bone_weights[std::size_t(v) * 4 + k] = w;
if (local > max_local_bone)
max_local_bone = local;
}
}
mesh_ptr->bone_count = static_cast<std::uint32_t>(max_local_bone + 1);
}
const StaticSkinMesh* mesh = mesh_ptr.get();
const auto* dest_begin = static_cast<const std::uint8_t*>(DestVertices);
const auto* dest_end = dest_begin + mesh->bind_vertices.size();
// Remove any stale overlapping slice from a previous model using the same buffer.
auto ov = g_dest_skin_slices.lower_bound(dest_begin);
if (ov != g_dest_skin_slices.begin())
{
auto prev = std::prev(ov);
if (prev->second.dest_end > dest_begin)
ov = prev;
}
while (ov != g_dest_skin_slices.end() && ov->second.dest_begin < dest_end)
{
if (ov->first != dest_begin)
ov = g_dest_skin_slices.erase(ov);
else
++ov;
}
DestSkinSlice& slice = g_dest_skin_slices[dest_begin];
if (slice.source != mesh || slice.vertex_count != mesh->vertex_count)
{
std::memcpy(DestVertices, mesh->bind_vertices.data(), mesh->bind_vertices.size());
slice.dest_begin = dest_begin;
slice.dest_end = dest_end;
slice.stride = mesh->out_stride;
slice.vertex_count = mesh->vertex_count;
slice.source = mesh;
}
slice.bone_matrices.resize(std::size_t(mesh->bone_count) * 16);
for (std::uint32_t b = 0; b < mesh->bone_count; ++b)
{
const int bone = MatrixIndices ? MatrixIndices[b] : int(b);
std::memcpy(&slice.bone_matrices[std::size_t(b) * 16], MatrixBuffer4x4 + std::size_t(bone) * 16, 16 * sizeof(float));
}
return;
}
for (int v = 0; v < VertexCount; ++v)
{
const uint8_t* src = static_cast<const uint8_t*>(SourceVertices) + size_t(v) * d.in.size;
@@ -863,6 +1032,7 @@ void GrannyDeformVertices(granny_mesh_deformer const* Deformer, granny_int32x co
}
}
// ── Model instances and controls ───────────────────────────────────────────────────────────────
struct granny_model_instance
+9 -2
View File
@@ -5,10 +5,15 @@
#include "EterLib/StdAfx.h"
#include <cstdint>
#include <atomic>
#include <vector>
inline std::atomic<std::uint64_t> mt_next_cpu_buffer_id{1};
struct MtCpuVertexBuffer : IDirect3DVertexBuffer8
{
const std::uint64_t id = mt_next_cpu_buffer_id.fetch_add(1, std::memory_order_relaxed);
std::uint64_t revision = 0;
std::vector<uint8_t> bytes;
DWORD fvf = 0;
@@ -19,11 +24,13 @@ struct MtCpuVertexBuffer : IDirect3DVertexBuffer8
*data = bytes.data() + offset;
return S_OK;
}
HRESULT Unlock() override { return S_OK; }
HRESULT Unlock() override { ++revision; return S_OK; }
};
struct MtCpuIndexBuffer : IDirect3DIndexBuffer8
{
const std::uint64_t id = mt_next_cpu_buffer_id.fetch_add(1, std::memory_order_relaxed);
std::uint64_t revision = 0;
std::vector<uint8_t> bytes;
D3DFORMAT format = D3DFMT_INDEX16;
@@ -34,7 +41,7 @@ struct MtCpuIndexBuffer : IDirect3DIndexBuffer8
*data = bytes.data() + offset;
return S_OK;
}
HRESULT Unlock() override { return S_OK; }
HRESULT Unlock() override { ++revision; return S_OK; }
};
// D3DXGetFVFVertexSize.
@@ -1,79 +0,0 @@
// Platform skeleton for EterLib/CullingManager.h (40250 EterLib/CullingManager.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
#include "EterLib/StdAfx.h"
#include "EterLib/CullingManager.h"
#include "../PlatformStub.h"
CCullingManager::CCullingManager()
{
MT_PLATFORM_STUB();
}
CCullingManager::~CCullingManager()
{
MT_PLATFORM_STUB();
}
auto CCullingManager::RayTraceCallback(const Vector3d &, const Vector3d &, float, const Vector3d &, SpherePack *) -> void
{
MT_PLATFORM_STUB();
}
auto CCullingManager::VisibilityCallback(const Frustum &, SpherePack *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto CCullingManager::RangeTestCallback(const Vector3d &, float, SpherePack *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto CCullingManager::Reset() -> void
{
MT_PLATFORM_STUB();
}
auto CCullingManager::Update() -> void
{
MT_PLATFORM_STUB();
}
// 40250 CullingManager.cpp:105. RenderGame calls it after SetPerspective; it hands the camera's view and
// the projection to CStateManager for the scene.
// PORT: BuildViewFrustum and m_Factory->FrustumTest follow in 40250; the sphere-pack culling (SphereLib)
// is not ported, so every registered object stays visible.
void CCullingManager::Process()
{
//DWORD time = ELTimer_GetMSec();
//Frustum f;
UpdateViewMatrix();
UpdateProjMatrix();
}
auto CCullingManager::FindRange(const Vector3d &, float) -> void
{
MT_PLATFORM_STUB();
}
auto CCullingManager::FindRay(const Vector3d &, const Vector3d &) -> void
{
MT_PLATFORM_STUB();
}
auto CCullingManager::FindRayDistance(const Vector3d &, const Vector3d &, float) -> void
{
MT_PLATFORM_STUB();
}
auto CCullingManager::Register(CGraphicObjectInstance *) -> CullingHandle
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<CullingHandle>();
}
auto CCullingManager::Unregister(CullingHandle) -> void
{
MT_PLATFORM_STUB();
}
@@ -98,6 +98,8 @@ int CGraphicDevice::Create(HWND hWnd, int iHres, int iVres, bool Windowed, int /
D3DXMatrixIdentity(&ms_matProj);
D3DXMatrixIdentity(&ms_matInverseView);
D3DXMatrixIdentity(&ms_matInverseViewYAxis);
D3DXMatrixIdentity(&ms_matWorld);
D3DXMatrixIdentity(&ms_matWorldView);
D3DXMatrixIdentity(&ms_matScreen0);
D3DXMatrixIdentity(&ms_matScreen1);
D3DXMatrixIdentity(&ms_matScreen2);
@@ -137,16 +137,22 @@ std::string UIRenderTextureNameFromHandle(const IDirect3DBaseTexture8* handle)
{
if (!handle) return {};
const auto* texture = static_cast<const IDirect3DTexture8*>(handle);
std::lock_guard<std::mutex> lock(g_memory_mutex);
if (const MemoryTexture* memory = live_memory_texture(texture))
return "mem:" + std::to_string(memory->id) + "@" + std::to_string(memory->revision);
const auto* file = static_cast<const FileTexture*>(texture);
return g_file_textures.count(file) ? file->name : std::string();
{
std::lock_guard<std::mutex> lock(g_memory_mutex);
if (const MemoryTexture* memory = live_memory_texture(texture))
return "mem:" + std::to_string(memory->id) + "@" + std::to_string(memory->revision);
const auto* file = static_cast<const FileTexture*>(texture);
if (g_file_textures.count(file))
return file->name;
}
return MtCpuTextureNameFromHandle(handle);
}
bool UIRenderMemoryTexture(const std::string& name, UIMemoryTexture* out)
{
if (name.compare(0, 4, "mem:") != 0 || !out) return false;
if (name.compare(0, 8, "mem:cpu_") == 0)
return MtCpuMemoryTexture(name, out);
const unsigned id = unsigned(std::strtoul(name.c_str() + 4, nullptr, 10));
std::lock_guard<std::mutex> lock(g_memory_mutex);
auto it = g_memory.find(id);
@@ -34,6 +34,7 @@ bool CGraphicIndexBuffer::Lock(void** pretIndices) const
void CGraphicIndexBuffer::Unlock() const
{
assert(m_lpd3dIdxBuf!=NULL);
static_cast<MtCpuIndexBuffer*>(m_lpd3dIdxBuf)->revision++;
}
bool CGraphicIndexBuffer::Lock(void** pretIndices)
@@ -47,6 +48,7 @@ bool CGraphicIndexBuffer::Lock(void** pretIndices)
void CGraphicIndexBuffer::Unlock()
{
assert(m_lpd3dIdxBuf!=NULL);
static_cast<MtCpuIndexBuffer*>(m_lpd3dIdxBuf)->revision++;
}
bool CGraphicIndexBuffer::Copy(int bufSize, const void* srcIndices)
@@ -54,6 +56,7 @@ bool CGraphicIndexBuffer::Copy(int bufSize, const void* srcIndices)
assert(m_lpd3dIdxBuf!=NULL);
memcpy(index_bytes(m_lpd3dIdxBuf), srcIndices, bufSize);
static_cast<MtCpuIndexBuffer*>(m_lpd3dIdxBuf)->revision++;
return true;
}
@@ -73,6 +76,7 @@ bool CGraphicIndexBuffer::Create(int faceCount, TFace* faces)
dstIndices[1]=curFace->indices[1];
dstIndices[2]=curFace->indices[2];
}
static_cast<MtCpuIndexBuffer*>(m_lpd3dIdxBuf)->revision++;
return true;
}
+77 -16
View File
@@ -3,6 +3,7 @@
#include "EterLib/StdAfx.h"
#include "EterLib/GrpScreen.h"
#include "EterLib/StateManager.h"
#include "EterLib/Camera.h"
#include "../PlatformStub.h"
#include "UIRenderCommands.h"
@@ -285,27 +286,75 @@ void CScreen::SetCursorPosition(int x, int y, int hres, int vres)
ms_Ray.SetDirection(-ms_vtPickRayDir, 51200.0f);
}
auto CScreen::GetCursorPosition(float *, float *, float *) -> bool
auto CScreen::GetCursorPosition(float * px, float * py, float * pz) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
if (!GetCursorXYPosition(px, py)) return false;
if (!GetCursorZPosition(pz)) return false;
return true;
}
auto CScreen::GetCursorXYPosition(float *, float *) -> bool
auto CScreen::GetCursorXYPosition(float * px, float * py) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
D3DXVECTOR3 v3Eye = CCameraManager::Instance().GetCurrentCamera()->GetEye();
TPosition posVertices[4];
posVertices[0] = TPosition(v3Eye.x - 90000000.0f, v3Eye.y + 90000000.0f, 0.0f);
posVertices[1] = TPosition(v3Eye.x - 90000000.0f, v3Eye.y - 90000000.0f, 0.0f);
posVertices[2] = TPosition(v3Eye.x + 90000000.0f, v3Eye.y + 90000000.0f, 0.0f);
posVertices[3] = TPosition(v3Eye.x + 90000000.0f, v3Eye.y - 90000000.0f, 0.0f);
static const WORD sc_awFillRectIndices[6] = { 0, 2, 1, 2, 3, 1, };
float u, v, t;
for (int i = 0; i < 2; ++i)
{
if (IntersectTriangle(ms_vtPickRayOrig, ms_vtPickRayDir,
posVertices[sc_awFillRectIndices[i * 3]],
posVertices[sc_awFillRectIndices[i * 3 + 1]],
posVertices[sc_awFillRectIndices[i * 3 + 2]],
&u, &v, &t))
{
*px = u;
*py = v;
return true;
}
}
return false;
}
auto CScreen::GetCursorZPosition(float *) -> bool
auto CScreen::GetCursorZPosition(float * pz) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
D3DXVECTOR3 v3Eye = CCameraManager::Instance().GetCurrentCamera()->GetEye();
TPosition posVertices[4];
posVertices[0] = TPosition(v3Eye.x - 90000000.0f, 0.0f, v3Eye.z + 90000000.0f);
posVertices[1] = TPosition(v3Eye.x - 90000000.0f, 0.0f, v3Eye.z - 90000000.0f);
posVertices[2] = TPosition(v3Eye.x + 90000000.0f, 0.0f, v3Eye.z + 90000000.0f);
posVertices[3] = TPosition(v3Eye.x + 90000000.0f, 0.0f, v3Eye.z - 90000000.0f);
static const WORD sc_awFillRectIndices[6] = { 0, 2, 1, 2, 3, 1, };
float u, v, t;
for (int i = 0; i < 2; ++i)
{
if (IntersectTriangle(ms_vtPickRayOrig, ms_vtPickRayDir,
posVertices[sc_awFillRectIndices[i * 3]],
posVertices[sc_awFillRectIndices[i * 3 + 1]],
posVertices[sc_awFillRectIndices[i * 3 + 2]],
&u, &v, &t))
{
*pz = t;
return true;
}
}
return false;
}
auto CScreen::GetPickingPosition(float, float *, float *, float *) -> void
auto CScreen::GetPickingPosition(float t, float * x, float * y, float * z) -> void
{
MT_PLATFORM_STUB();
*x = ms_vtPickRayOrig.x + ms_vtPickRayDir.x * t;
*y = ms_vtPickRayOrig.y + ms_vtPickRayDir.y * t;
*z = ms_vtPickRayOrig.z + ms_vtPickRayDir.z * t;
}
// 40250 GrpScreen.cpp:749-779.
@@ -313,7 +362,7 @@ void CScreen::ProjectPosition(float x, float y, float z, float * pfX, float * pf
{
D3DXVECTOR3 Input(x, y, z);
D3DXVECTOR3 Output;
D3DXVec3Project(&Output, &Input, &ms_Viewport, &ms_matProj, &ms_matView, &ms_matWorld);
D3DXVec3Project(&Output, &Input, &ms_Viewport, &ms_matProj, &ms_matView, &ms_matIdentity);
*pfX = Output.x;
*pfY = Output.y;
@@ -323,7 +372,7 @@ void CScreen::ProjectPosition(float x, float y, float z, float * pfX, float * pf
{
D3DXVECTOR3 Input(x, y, z);
D3DXVECTOR3 Output;
D3DXVec3Project(&Output, &Input, &ms_Viewport, &ms_matProj, &ms_matView, &ms_matWorld);
D3DXVec3Project(&Output, &Input, &ms_Viewport, &ms_matProj, &ms_matView, &ms_matIdentity);
*pfX = Output.x;
*pfY = Output.y;
@@ -334,7 +383,7 @@ void CScreen::UnprojectPosition(float x, float y, float z, float * pfX, float *
{
D3DXVECTOR3 Input(x, y, z);
D3DXVECTOR3 Output;
D3DXVec3Unproject(&Output, &Input, &ms_Viewport, &ms_matProj, &ms_matView, &ms_matWorld);
D3DXVec3Unproject(&Output, &Input, &ms_Viewport, &ms_matProj, &ms_matView, &ms_matIdentity);
*pfX = Output.x;
*pfY = Output.y;
@@ -355,12 +404,24 @@ auto CScreen::RestoreDevice() -> BOOL
auto CScreen::BuildViewFrustum() -> void
{
MT_PLATFORM_STUB();
CCamera* pkCamera = CCameraManager::Instance().GetCurrentCamera();
if (!pkCamera)
return;
const D3DXVECTOR3& c_rv3Eye = pkCamera->GetEye();
const D3DXVECTOR3& c_rv3View = pkCamera->GetView();
auto vv = ms_matView * ms_matProj;
ms_frustum.BuildViewFrustum2(
vv,
ms_fNearY,
ms_fFarY,
ms_fFieldOfView,
ms_fAspect,
c_rv3Eye, c_rv3View);
}
auto CScreen::Identity() -> void
{
MT_PLATFORM_STUB();
STATEMANAGER.SetTransform(D3DTS_WORLD, &ms_matIdentity);
}
decltype(CScreen::ms_diffuseColor) CScreen::ms_diffuseColor{};
@@ -49,6 +49,7 @@ bool CGraphicVertexBuffer::Unlock() const
{
if (!m_lpd3dVB)
return false;
static_cast<MtCpuVertexBuffer*>(m_lpd3dVB)->revision++;
return true;
}
@@ -83,6 +84,7 @@ bool CGraphicVertexBuffer::Unlock()
{
if (!m_lpd3dVB)
return false;
static_cast<MtCpuVertexBuffer*>(m_lpd3dVB)->revision++;
return true;
}
+20 -11
View File
@@ -35,34 +35,43 @@ CInputKeyboard::~CInputKeyboard()
auto CInputKeyboard::InitializeKeyboard(HWND) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
ResetKeyboard();
return true;
}
auto CInputKeyboard::UpdateKeyboard() -> void
{
MT_PLATFORM_STUB();
}
auto CInputKeyboard::ResetKeyboard() -> void
{
MT_PLATFORM_STUB();
memset(ms_bPressedKey, 0, sizeof(ms_bPressedKey));
memset(ms_diks, 0, sizeof(ms_diks));
}
auto CInputKeyboard::IsPressed(int) -> bool
auto CInputKeyboard::IsPressed(int iIndex) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
if (iIndex >= 0 && iIndex < 256)
return (ms_diks[iIndex] & 0x80) != 0;
return false;
}
auto CInputKeyboard::KeyDown(int) -> void
auto CInputKeyboard::KeyDown(int iIndex) -> void
{
MT_PLATFORM_STUB();
if (iIndex >= 0 && iIndex < 256)
{
ms_bPressedKey[iIndex] = true;
ms_diks[iIndex] = (char)0x80;
}
}
auto CInputKeyboard::KeyUp(int) -> void
auto CInputKeyboard::KeyUp(int iIndex) -> void
{
MT_PLATFORM_STUB();
if (iIndex >= 0 && iIndex < 256)
{
ms_bPressedKey[iIndex] = false;
ms_diks[iIndex] = 0;
}
}
decltype(CInputKeyboard::ms_lpKeyboard) CInputKeyboard::ms_lpKeyboard{};
@@ -5,12 +5,17 @@
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iterator>
#include <mutex>
#include <unordered_map>
namespace {
std::mutex g_draws_mutex;
std::vector<Render3DDraw> g_draws;
int g_native_terrain_override = -1;
struct VertexLayout {
unsigned stride = 0;
@@ -69,9 +74,8 @@ int format_bytes(D3DFORMAT format)
}
// PORT: device-created resources live in CPU memory with D3D reference counting. Textures made by
// CreateTexture (CTerrain's splat alpha maps and attribute marks, CSnowEnvironment's blur targets)
// keep their mip levels as bytes; nothing samples them because terrain and offscreen passes are
// drawn by the Godot side.
// CreateTexture resources keep their mip levels as bytes. Terrain alpha maps and the character
// shadow render target are sampled by the native renderer through MtCpuMemoryTexture.
struct CpuTexture;
struct CpuSurface final : IDirect3DSurface8
@@ -89,13 +93,32 @@ struct CpuSurface final : IDirect3DSurface8
HRESULT UnlockRect() override { return S_OK; }
};
struct CpuTexture;
std::mutex g_cpu_tex_mutex;
std::unordered_map<unsigned, CpuTexture*> g_cpu_textures;
unsigned g_next_cpu_tex_id = 1;
struct CpuTexture final : IDirect3DTexture8
{
struct Level { D3DSURFACE_DESC desc; std::vector<uint8_t> bytes; };
unsigned id = 0;
std::uint32_t revision = 1;
ULONG refs = 1;
std::vector<Level> levels;
CpuTexture()
{
std::lock_guard<std::mutex> lock(g_cpu_tex_mutex);
id = g_next_cpu_tex_id++;
g_cpu_textures[id] = this;
}
~CpuTexture()
{
std::lock_guard<std::mutex> lock(g_cpu_tex_mutex);
g_cpu_textures.erase(id);
}
ULONG AddRef() override { return ++refs; }
ULONG Release() override
{
@@ -132,7 +155,14 @@ struct CpuTexture final : IDirect3DTexture8
locked->pBits = levels[level].bytes.data();
return S_OK;
}
HRESULT UnlockRect(UINT level) override { return level < levels.size() ? S_OK : E_FAIL; }
HRESULT UnlockRect(UINT level) override
{
if (level >= levels.size())
return E_FAIL;
if (level == 0)
++revision;
return S_OK;
}
};
ULONG CpuSurface::Release()
@@ -217,6 +247,22 @@ public:
m_renderTarget->AddRef();
m_depthStencil = m_depthBuffer;
m_depthStencil->AddRef();
m_viewport = { 0, 0, (DWORD)width, (DWORD)height, 0.0f, 1.0f };
m_renderStates[D3DRS_TEXTUREFACTOR] = 0xFFFFFFFF;
m_renderStates[D3DRS_ZENABLE] = TRUE;
m_renderStates[D3DRS_ZWRITEENABLE] = TRUE;
m_renderStates[D3DRS_CULLMODE] = D3DCULL_CCW;
m_renderStates[D3DRS_LIGHTING] = TRUE;
m_renderStates[D3DRS_SRCBLEND] = D3DBLEND_ONE;
m_renderStates[D3DRS_DESTBLEND] = D3DBLEND_ZERO;
m_renderStates[D3DRS_ALPHAFUNC] = D3DCMP_ALWAYS;
m_stageStates[0][D3DTSS_COLOROP] = D3DTOP_MODULATE;
m_stageStates[0][D3DTSS_COLORARG1] = D3DTA_TEXTURE;
m_stageStates[0][D3DTSS_COLORARG2] = D3DTA_CURRENT;
m_stageStates[0][D3DTSS_ALPHAOP] = D3DTOP_SELECTARG1;
m_stageStates[0][D3DTSS_ALPHAARG1] = D3DTA_TEXTURE;
m_stageStates[1][D3DTSS_COLOROP] = D3DTOP_DISABLE;
m_stageStates[1][D3DTSS_ALPHAOP] = D3DTOP_DISABLE;
}
~RecordingDevice() override
{
@@ -254,7 +300,14 @@ public:
}
HRESULT GetViewport(D3DVIEWPORT8* viewport) override
{
*viewport = { 0, 0, DWORD(m_width), DWORD(m_height), 0.0f, 1.0f };
if (viewport)
*viewport = m_viewport;
return S_OK;
}
HRESULT SetViewport(const D3DVIEWPORT8* viewport) override
{
if (viewport)
m_viewport = *viewport;
return S_OK;
}
// PORT: CPU memory has no texture budget; report the 64MB of a mid-range 2004 card.
@@ -340,8 +393,8 @@ public:
*out = m_depthStencil;
return S_OK;
}
// PORT: an offscreen target (CSnowEnvironment's blur pass) swallows its draws; only the back
// buffer's draws are recorded for the Godot renderer.
// Character shadow targets are rasterized below; other offscreen targets still need a
// separate effect-specific implementation (snow blur is disabled by default).
HRESULT SetRenderTarget(IDirect3DSurface8* target, IDirect3DSurface8* depth) override
{
if (target)
@@ -358,7 +411,28 @@ public:
}
return S_OK;
}
HRESULT Clear(DWORD, const D3DRECT*, DWORD, D3DCOLOR, float, DWORD) override { return S_OK; }
HRESULT Clear(DWORD, const D3DRECT*, DWORD flags, D3DCOLOR color, float depth, DWORD) override
{
if (m_renderTarget == m_backBuffer)
return S_OK;
auto* surface = static_cast<CpuSurface*>(m_renderTarget);
if (!surface->parent || surface->level != 0 || surface->desc.Format != D3DFMT_R5G6B5)
return S_OK;
const size_t pixels = size_t(surface->desc.Width) * surface->desc.Height;
if (flags & D3DCLEAR_TARGET)
{
const std::uint16_t rgb565 = std::uint16_t((((color >> 16) & 255u) >> 3) << 11 |
(((color >> 8) & 255u) >> 2) << 5 | ((color & 255u) >> 3));
auto& bytes = surface->parent->levels[0].bytes;
bytes.resize(pixels * 2);
for (size_t i = 0; i < pixels; ++i)
std::memcpy(bytes.data() + i * 2, &rgb565, 2);
++surface->parent->revision;
}
if (flags & D3DCLEAR_ZBUFFER)
m_offscreenDepth.assign(pixels, depth);
return S_OK;
}
HRESULT SetRenderState(D3DRENDERSTATETYPE state, DWORD value) override
{
if (unsigned(state) < kRenderStates)
@@ -406,7 +480,8 @@ public:
std::vector<std::uint32_t> indices(count);
for (UINT i = 0; i < count; ++i)
indices[i] = startVertex + i;
record(type, primitiveCount, m_stream->bytes.data(), m_stream->bytes.size(), m_streamStride, indices);
record(type, primitiveCount, m_stream->bytes.data(), m_stream->bytes.size(), m_streamStride, indices,
m_stream, nullptr, startVertex);
return S_OK;
}
HRESULT DrawIndexedPrimitive(D3DPRIMITIVETYPE type, UINT, UINT, UINT startIndex, UINT primitiveCount) override
@@ -417,7 +492,8 @@ public:
if (!read_indices(m_indices->bytes.data(), m_indices->bytes.size(), m_indices->format, startIndex,
index_count(type, primitiveCount), m_baseVertex, &indices))
return E_FAIL;
record(type, primitiveCount, m_stream->bytes.data(), m_stream->bytes.size(), m_streamStride, indices);
record(type, primitiveCount, m_stream->bytes.data(), m_stream->bytes.size(), m_streamStride, indices,
m_stream, m_indices, startIndex);
return S_OK;
}
HRESULT DrawPrimitiveUP(D3DPRIMITIVETYPE type, UINT primitiveCount, const void* vertices, UINT stride) override
@@ -488,6 +564,92 @@ private:
transform_point(&a[r * 4], b, &out[r * 4]);
}
// The 40250 shadow pass renders a flat grey silhouette into an R5G6B5 target.
// Rasterizing that small target here keeps the D3D render-target sequence and lets
// the normal memory-texture path upload the result for terrain/object projection.
void rasterize_shadow(const Render3DDraw& draw)
{
auto* surface = static_cast<CpuSurface*>(m_renderTarget);
if (!surface->parent || surface->level != 0 || surface->desc.Format != D3DFMT_R5G6B5 || draw.lines)
return;
const int width = int(surface->desc.Width), height = int(surface->desc.Height);
if (width <= 0 || height <= 0 || draw.indices.size() < 3)
return;
auto& bytes = surface->parent->levels[0].bytes;
if (bytes.size() < size_t(width) * height * 2)
return;
if (m_offscreenDepth.size() != size_t(width) * height)
m_offscreenDepth.assign(size_t(width) * height, 1.0f);
float worldView[16], mvp[16];
multiply(draw.world, draw.view, worldView);
multiply(worldView, draw.proj, mvp);
struct Point { float x, y, z; bool valid; };
std::vector<Point> points(draw.positions.size() / 3);
for (size_t i = 0; i < points.size(); ++i)
{
float p[4] = { draw.positions[i * 3], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2], 1.0f };
if (!draw.bone_matrices.empty() && draw.bone_weights.size() >= (i + 1) * 4 &&
draw.bone_indices.size() >= (i + 1) * 4)
{
float skinned[4] = {};
float total = 0.0f;
for (int b = 0; b < 4; ++b)
{
const float weight = draw.bone_weights[i * 4 + b];
const size_t bone = draw.bone_indices[i * 4 + b];
if (weight <= 0.0f || (bone + 1) * 16 > draw.bone_matrices.size()) continue;
float transformed[4];
transform_point(p, draw.bone_matrices.data() + bone * 16, transformed);
for (int c = 0; c < 4; ++c) skinned[c] += transformed[c] * weight;
total += weight;
}
if (total > 0.0f) { p[0] = skinned[0]; p[1] = skinned[1]; p[2] = skinned[2]; }
}
float clip[4];
transform_point(p, mvp, clip);
const bool valid = clip[3] > 0.00001f;
const float invW = valid ? 1.0f / clip[3] : 0.0f;
points[i] = { draw.viewport[0] + (clip[0] * invW * 0.5f + 0.5f) * draw.viewport[2],
draw.viewport[1] + (0.5f - clip[1] * invW * 0.5f) * draw.viewport[3],
clip[2] * invW, valid };
}
const std::uint32_t color = m_renderStates[D3DRS_TEXTUREFACTOR];
const std::uint16_t rgb565 = std::uint16_t((((color >> 16) & 255u) >> 3) << 11 |
(((color >> 8) & 255u) >> 2) << 5 | ((color & 255u) >> 3));
auto edge = [](const Point& a, const Point& b, float x, float y) {
return (x - a.x) * (b.y - a.y) - (y - a.y) * (b.x - a.x);
};
for (size_t i = 0; i + 2 < draw.indices.size(); i += 3)
{
if (draw.indices[i] >= points.size() || draw.indices[i + 1] >= points.size() ||
draw.indices[i + 2] >= points.size()) continue;
const Point& a = points[draw.indices[i]];
const Point& b = points[draw.indices[i + 1]];
const Point& c = points[draw.indices[i + 2]];
if (!a.valid || !b.valid || !c.valid) continue;
const float area = edge(a, b, c.x, c.y);
if (std::fabs(area) < 0.00001f) continue;
const int x0 = std::max(0, int(std::floor(std::min({a.x, b.x, c.x}))));
const int y0 = std::max(0, int(std::floor(std::min({a.y, b.y, c.y}))));
const int x1 = std::min(width - 1, int(std::ceil(std::max({a.x, b.x, c.x}))));
const int y1 = std::min(height - 1, int(std::ceil(std::max({a.y, b.y, c.y}))));
for (int y = y0; y <= y1; ++y)
for (int x = x0; x <= x1; ++x)
{
const float px = float(x) + 0.5f, py = float(y) + 0.5f;
const float wa = edge(b, c, px, py) / area;
const float wb = edge(c, a, px, py) / area;
const float wc = 1.0f - wa - wb;
if (wa < 0.0f || wb < 0.0f || wc < 0.0f) continue;
const float z = wa * a.z + wb * b.z + wc * c.z;
const size_t pixel = size_t(y) * width + x;
if (z < 0.0f || z > 1.0f || z > m_offscreenDepth[pixel]) continue;
m_offscreenDepth[pixel] = z;
std::memcpy(bytes.data() + pixel * 2, &rgb565, 2);
}
}
}
// Each two-triangle quad of a UI-space draw becomes an image command: its screen corners through
// world * view * projection, its stage-0 texture (or D3DTA_TFACTOR colour when stage 0 selects it)
// and, when stage 1 generates coordinates from the camera-space position through D3DTS_TEXTURE1
@@ -585,9 +747,10 @@ private:
}
void record(D3DPRIMITIVETYPE type, UINT primitives, const uint8_t* vertices, size_t vertexBytes, UINT stride,
const std::vector<std::uint32_t>& indices)
const std::vector<std::uint32_t>& indices, const MtCpuVertexBuffer* source_vertex = nullptr,
const MtCpuIndexBuffer* source_index = nullptr, UINT source_first_index = 0)
{
if (type == D3DPT_POINTLIST || indices.empty() || !vertices || m_renderTarget != m_backBuffer)
if (type == D3DPT_POINTLIST || indices.empty() || !vertices)
return;
VertexLayout layout = fvf_layout(m_fvf);
if (!stride)
@@ -602,7 +765,7 @@ private:
// An orthographic projection over untransformed vertices is a UI-space draw (CPythonMiniMap's
// terrain tiles under CPythonGraphic::SetOrtho2D): it joins the UI command stream in order.
if (!layout.rhw && m_transforms[D3DTS_PROJECTION][11] == 0.0f)
if (m_renderTarget == m_backBuffer && !layout.rhw && m_transforms[D3DTS_PROJECTION][11] == 0.0f)
{
record_ui_quads(type, vertices, vertexBytes, stride, layout, indices);
return;
@@ -614,6 +777,10 @@ private:
std::memcpy(draw.proj, m_transforms[D3DTS_PROJECTION], sizeof(draw.proj));
draw.texture0 = UIRenderTextureNameFromHandle(m_textures[0]);
draw.texture1 = UIRenderTextureNameFromHandle(m_textures[1]);
draw.viewport[0] = float(m_viewport.X);
draw.viewport[1] = float(m_viewport.Y);
draw.viewport[2] = float(m_viewport.Width);
draw.viewport[3] = float(m_viewport.Height);
draw.pretransformed = layout.rhw;
draw.lines = type == D3DPT_LINELIST || type == D3DPT_LINESTRIP;
@@ -627,47 +794,221 @@ private:
if ((size_t(hi) + 1) * stride > vertexBytes)
return;
const size_t count = size_t(hi - lo) + 1;
draw.positions.resize(count * 3);
if (layout.rhw) draw.rhw.resize(count);
if (layout.normal >= 0) draw.normals.resize(count * 3);
if (layout.uv0 >= 0) draw.uv0.resize(count * 2);
if (layout.uv1 >= 0) draw.uv1.resize(count * 2);
if (layout.diffuse >= 0) draw.diffuse.resize(count);
for (size_t i = 0; i < count; ++i)
const bool texgen_cam_pos0 = layout.uv0 < 0 && m_textures[0] != nullptr &&
(m_stageStates[0][D3DTSS_TEXCOORDINDEX] & 0xFFFF0000u) == D3DTSS_TCI_CAMERASPACEPOSITION &&
m_stageStates[0][D3DTSS_TEXTURETRANSFORMFLAGS] == D3DTTFF_COUNT2;
const bool texgen_cam_pos1 = layout.uv1 < 0 && m_textures[1] != nullptr &&
(m_stageStates[1][D3DTSS_TEXCOORDINDEX] & 0xFFFF0000u) == D3DTSS_TCI_CAMERASPACEPOSITION &&
m_stageStates[1][D3DTSS_TEXTURETRANSFORMFLAGS] == D3DTTFF_COUNT2;
const bool tex_xform0 = layout.uv0 >= 0 && m_textures[0] != nullptr &&
(m_stageStates[0][D3DTSS_TEXCOORDINDEX] & 0xFFFF0000u) == 0 &&
m_stageStates[0][D3DTSS_TEXTURETRANSFORMFLAGS] == D3DTTFF_COUNT2;
GpuSkinSubrangeView skin_view;
const bool gpu_skinned = source_vertex != nullptr &&
LookupGpuSkinSubrange(vertices, stride, lo, hi, &skin_view);
if (source_vertex)
{
const uint8_t* v = vertices + (lo + i) * stride;
std::memcpy(&draw.positions[i * 3], v, 12);
if (layout.rhw) std::memcpy(&draw.rhw[i], v + 12, 4);
if (layout.normal >= 0) std::memcpy(&draw.normals[i * 3], v + layout.normal, 12);
if (layout.uv0 >= 0) std::memcpy(&draw.uv0[i * 2], v + layout.uv0, 8);
if (layout.uv1 >= 0) std::memcpy(&draw.uv1[i * 2], v + layout.uv1, 8);
if (layout.diffuse >= 0) std::memcpy(&draw.diffuse[i], v + layout.diffuse, 4);
// The original buffers outlive individual draw calls. Keep their identity separate from
// Unlock revisions so a static surface can keep its Godot mesh across frames.
auto mix = [](std::uint64_t hash, std::uint64_t value) {
return (hash ^ value) * 1099511628211ull;
};
std::uint64_t key = 14695981039346656037ull;
if (gpu_skinned)
{
key = mix(key, skin_view.source_mesh_key);
key = mix(key, source_index ? source_index->id : 0);
key = mix(key, source_first_index);
key = mix(key, lo - skin_view.mesh_base_vertex);
key = mix(key, hi - skin_view.mesh_base_vertex);
key = mix(key, primitives);
key = mix(key, type);
key = mix(key, stride);
key = mix(key, m_fvf);
draw.geometry_key = (key & 0x7FFFFFFFFFFFFFFFull) | 1ull;
draw.geometry_revision = 1ull;
}
else
{
key = mix(key, source_vertex->id);
key = mix(key, source_index ? source_index->id : 0);
key = mix(key, source_first_index);
key = mix(key, lo);
key = mix(key, hi);
key = mix(key, primitives);
key = mix(key, type);
key = mix(key, stride);
key = mix(key, m_fvf);
if (texgen_cam_pos0)
key = mix(key, static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(m_textures[0])));
if (texgen_cam_pos1)
key = mix(key, static_cast<std::uint64_t>(reinterpret_cast<std::uintptr_t>(m_textures[1])));
draw.geometry_key = (key & 0x7FFFFFFFFFFFFFFFull) | 1ull;
std::uint64_t revision = mix(14695981039346656037ull, source_vertex->revision);
revision = mix(revision, source_index ? source_index->revision : 0);
if (tex_xform0)
{
const float* m = m_transforms[D3DTS_TEXTURE0];
for (int mi : { 0, 1, 4, 5, 8, 9, 12, 13 })
{
std::uint32_t bits = 0;
std::memcpy(&bits, &m[mi], sizeof(bits));
revision = mix(revision, bits);
}
}
// Camera-space texture coordinates are baked into the captured geometry.
// The terrain splat and character-shadow matrices can change while the
// vertex/index buffers remain unchanged, so they are part of its revision.
for (int stage = 0; stage < 2; ++stage)
{
if ((stage == 0 && !texgen_cam_pos0) || (stage == 1 && !texgen_cam_pos1))
continue;
float worldView[16], worldTexture[16];
multiply(m_transforms[256], m_transforms[D3DTS_VIEW], worldView);
multiply(worldView, m_transforms[stage == 0 ? D3DTS_TEXTURE0 : D3DTS_TEXTURE1], worldTexture);
for (int mi : { 0, 1, 4, 5, 8, 9, 12, 13 })
{
std::uint32_t bits = 0;
std::memcpy(&bits, &worldTexture[mi], sizeof(bits));
revision = mix(revision, bits);
}
}
draw.geometry_revision = revision & 0x7FFFFFFFFFFFFFFFull;
}
}
const std::uint64_t frame_id = UIRenderFrameId();
auto cached = draw.geometry_key ? m_geometry_cache.find(draw.geometry_key) : m_geometry_cache.end();
const bool reuse = cached != m_geometry_cache.end() && !cached->second.changing &&
cached->second.revision == draw.geometry_revision;
if (reuse)
{
cached->second.last_used = frame_id;
const Render3DDraw& prior = cached->second.geometry;
draw.positions = prior.positions;
draw.rhw = prior.rhw;
draw.normals = prior.normals;
draw.uv0 = prior.uv0;
draw.uv1 = prior.uv1;
draw.diffuse = prior.diffuse;
draw.indices = prior.indices;
draw.bone_indices = prior.bone_indices;
draw.bone_weights = prior.bone_weights;
}
else
{
draw.positions.resize(count * 3);
if (layout.rhw) draw.rhw.resize(count);
if (layout.normal >= 0) draw.normals.resize(count * 3);
if (layout.uv0 >= 0) draw.uv0.resize(count * 2);
if (layout.uv1 >= 0) draw.uv1.resize(count * 2);
if (layout.diffuse >= 0) draw.diffuse.resize(count);
for (size_t i = 0; i < count; ++i)
{
const uint8_t* v = vertices + (lo + i) * stride;
std::memcpy(&draw.positions[i * 3], v, 12);
if (layout.rhw) std::memcpy(&draw.rhw[i], v + 12, 4);
if (layout.normal >= 0) std::memcpy(&draw.normals[i * 3], v + layout.normal, 12);
if (layout.uv0 >= 0) std::memcpy(&draw.uv0[i * 2], v + layout.uv0, 8);
if (layout.uv1 >= 0) std::memcpy(&draw.uv1[i * 2], v + layout.uv1, 8);
if (layout.diffuse >= 0) std::memcpy(&draw.diffuse[i], v + layout.diffuse, 4);
}
if (texgen_cam_pos0)
{
float worldView[16], worldTex0[16];
multiply(m_transforms[256], m_transforms[D3DTS_VIEW], worldView);
multiply(worldView, m_transforms[D3DTS_TEXTURE0], worldTex0);
draw.uv0.resize(count * 2);
const bool clamp0_u = m_stageStates[0][D3DTSS_ADDRESSU] == D3DTADDRESS_CLAMP;
const bool clamp0_v = m_stageStates[0][D3DTSS_ADDRESSV] == D3DTADDRESS_CLAMP;
for (size_t i = 0; i < count; ++i)
{
const float pos[4] = { draw.positions[i * 3 + 0], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2], 1.0f };
float tc[4];
transform_point(pos, worldTex0, tc);
draw.uv0[i * 2 + 0] = clamp0_u ? std::clamp(tc[0], 0.5f / 256.0f, 255.5f / 256.0f) : tc[0];
draw.uv0[i * 2 + 1] = clamp0_v ? std::clamp(tc[1], 0.5f / 256.0f, 255.5f / 256.0f) : tc[1];
}
}
else if (tex_xform0)
{
const float* m = m_transforms[D3DTS_TEXTURE0];
for (size_t i = 0; i < count; ++i)
{
const float u = draw.uv0[i * 2 + 0];
const float v = draw.uv0[i * 2 + 1];
draw.uv0[i * 2 + 0] = u * m[0] + v * m[4] + m[8] + m[12];
draw.uv0[i * 2 + 1] = u * m[1] + v * m[5] + m[9] + m[13];
}
}
if (texgen_cam_pos1)
{
float worldView[16], worldTex1[16];
multiply(m_transforms[256], m_transforms[D3DTS_VIEW], worldView);
multiply(worldView, m_transforms[D3DTS_TEXTURE1], worldTex1);
draw.uv1.resize(count * 2);
for (size_t i = 0; i < count; ++i)
{
const float pos[4] = { draw.positions[i * 3 + 0], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2], 1.0f };
float tc[4];
transform_point(pos, worldTex1, tc);
draw.uv1[i * 2 + 0] = tc[0];
draw.uv1[i * 2 + 1] = tc[1];
}
}
if (gpu_skinned)
{
const std::size_t rel_lo = std::size_t(lo - skin_view.mesh_base_vertex);
draw.bone_indices.resize(count * 4);
draw.bone_weights.resize(count * 4);
std::memcpy(draw.bone_indices.data(), skin_view.bone_indices + rel_lo * 4, count * 4 * sizeof(std::uint8_t));
std::memcpy(draw.bone_weights.data(), skin_view.bone_weights + rel_lo * 4, count * 4 * sizeof(float));
}
// Strips and fans become lists; D3D flips the winding of every odd strip triangle.
switch (type)
{
case D3DPT_TRIANGLESTRIP:
for (UINT i = 0; i < primitives; ++i)
{
const std::uint32_t a = indices[i] - lo, b = indices[i + 1] - lo, c = indices[i + 2] - lo;
if (i & 1) draw.indices.insert(draw.indices.end(), { b, a, c });
else draw.indices.insert(draw.indices.end(), { a, b, c });
}
break;
case D3DPT_TRIANGLEFAN:
for (UINT i = 0; i < primitives; ++i)
draw.indices.insert(draw.indices.end(), { indices[0] - lo, indices[i + 1] - lo, indices[i + 2] - lo });
break;
case D3DPT_LINESTRIP:
for (UINT i = 0; i < primitives; ++i)
draw.indices.insert(draw.indices.end(), { indices[i] - lo, indices[i + 1] - lo });
break;
default:
for (std::uint32_t index : indices)
draw.indices.push_back(index - lo);
break;
}
if (cached != m_geometry_cache.end())
{
cached->second.last_used = frame_id;
if (!cached->second.changing && cached->second.revision != draw.geometry_revision)
{
cached->second.changing = true;
cached->second.geometry = Render3DDraw();
}
}
else if (draw.geometry_key && cached == m_geometry_cache.end())
m_geometry_cache.emplace(draw.geometry_key, GeometryCacheEntry{draw.geometry_revision, frame_id, false, draw});
}
if (gpu_skinned && skin_view.bone_matrices && skin_view.bone_count > 0)
draw.bone_matrices.assign(skin_view.bone_matrices, skin_view.bone_matrices + std::size_t(skin_view.bone_count) * 16);
if (frame_id >= m_cache_pruned_frame + 120)
{
m_cache_pruned_frame = frame_id;
for (auto it = m_geometry_cache.begin(); it != m_geometry_cache.end();)
it = frame_id - it->second.last_used > 120 ? m_geometry_cache.erase(it) : std::next(it);
}
// Strips and fans become lists; D3D flips the winding of every odd strip triangle.
switch (type)
{
case D3DPT_TRIANGLESTRIP:
for (UINT i = 0; i < primitives; ++i)
{
const std::uint32_t a = indices[i] - lo, b = indices[i + 1] - lo, c = indices[i + 2] - lo;
if (i & 1) draw.indices.insert(draw.indices.end(), { b, a, c });
else draw.indices.insert(draw.indices.end(), { a, b, c });
}
break;
case D3DPT_TRIANGLEFAN:
for (UINT i = 0; i < primitives; ++i)
draw.indices.insert(draw.indices.end(), { indices[0] - lo, indices[i + 1] - lo, indices[i + 2] - lo });
break;
case D3DPT_LINESTRIP:
for (UINT i = 0; i < primitives; ++i)
draw.indices.insert(draw.indices.end(), { indices[i] - lo, indices[i + 1] - lo });
break;
default:
for (std::uint32_t index : indices)
draw.indices.push_back(index - lo);
break;
}
draw.alpha_blend = m_renderStates[D3DRS_ALPHABLENDENABLE];
draw.src_blend = m_renderStates[D3DRS_SRCBLEND];
@@ -682,7 +1023,17 @@ private:
draw.lighting = m_renderStates[D3DRS_LIGHTING];
draw.texture_factor = m_renderStates[D3DRS_TEXTUREFACTOR];
draw.fog_enable = m_renderStates[D3DRS_FOGENABLE];
draw.fog_color = m_renderStates[D3DRS_FOGCOLOR];
draw.fog_vertex_mode = m_renderStates[D3DRS_FOGVERTEXMODE];
draw.fog_table_mode = m_renderStates[D3DRS_FOGTABLEMODE];
draw.fog_range_enable = m_renderStates[D3DRS_RANGEFOGENABLE];
std::memcpy(&draw.fog_start, &m_renderStates[D3DRS_FOGSTART], sizeof(float));
std::memcpy(&draw.fog_end, &m_renderStates[D3DRS_FOGEND], sizeof(float));
std::memcpy(&draw.fog_density, &m_renderStates[D3DRS_FOGDENSITY], sizeof(float));
draw.ambient = m_renderStates[D3DRS_AMBIENT];
draw.diffuse_material_source = m_renderStates[D3DRS_DIFFUSEMATERIALSOURCE];
draw.ambient_material_source = m_renderStates[D3DRS_AMBIENTMATERIALSOURCE];
draw.color_vertex = m_renderStates[D3DRS_COLORVERTEX];
for (int stage = 0; stage < 2; ++stage)
{
draw.color_op[stage] = m_stageStates[stage][D3DTSS_COLOROP];
@@ -691,11 +1042,16 @@ private:
draw.alpha_op[stage] = m_stageStates[stage][D3DTSS_ALPHAOP];
draw.alpha_arg1[stage] = m_stageStates[stage][D3DTSS_ALPHAARG1];
draw.alpha_arg2[stage] = m_stageStates[stage][D3DTSS_ALPHAARG2];
draw.address_u[stage] = m_stageStates[stage][D3DTSS_ADDRESSU];
draw.address_v[stage] = m_stageStates[stage][D3DTSS_ADDRESSV];
draw.min_filter[stage] = m_stageStates[stage][D3DTSS_MINFILTER];
draw.mag_filter[stage] = m_stageStates[stage][D3DTSS_MAGFILTER];
draw.mip_filter[stage] = m_stageStates[stage][D3DTSS_MIPFILTER];
}
copy_color(draw.material_diffuse, m_material.Diffuse);
copy_color(draw.material_ambient, m_material.Ambient);
copy_color(draw.material_emissive, m_material.Emissive);
if (m_lightEnabled[0] && m_lights[0].Type == D3DLIGHT_DIRECTIONAL)
if (m_lightEnabled[0] && (m_lights[0].Type == D3DLIGHT_DIRECTIONAL || m_lights[0].Type == D3DLIGHT_SPOT))
{
draw.light0 = true;
draw.light0_direction[0] = m_lights[0].Direction.x;
@@ -704,11 +1060,37 @@ private:
copy_color(draw.light0_diffuse, m_lights[0].Diffuse);
copy_color(draw.light0_ambient, m_lights[0].Ambient);
}
Render3DAdd(std::move(draw));
for (int i = 0; i < 2; ++i)
{
if (!m_lightEnabled[i]) continue;
const D3DLIGHT8& source = m_lights[i];
auto& light = draw.lights[i];
light.type = source.Type;
light.position[0] = source.Position.x;
light.position[1] = source.Position.y;
light.position[2] = source.Position.z;
light.direction[0] = source.Direction.x;
light.direction[1] = source.Direction.y;
light.direction[2] = source.Direction.z;
copy_color(light.diffuse, source.Diffuse);
copy_color(light.ambient, source.Ambient);
light.attenuation[0] = source.Attenuation0;
light.attenuation[1] = source.Attenuation1;
light.attenuation[2] = source.Attenuation2;
light.range = source.Range;
light.theta = source.Theta;
light.phi = source.Phi;
light.falloff = source.Falloff;
}
if (m_renderTarget == m_backBuffer)
Render3DAdd(std::move(draw));
else
rasterize_shadow(draw);
}
ULONG m_refs = 1;
int m_width, m_height;
D3DVIEWPORT8 m_viewport = {};
float m_transforms[kTransforms][16];
DWORD m_renderStates[kRenderStates] = {};
DWORD m_stageStates[kStages][kStageStates] = {};
@@ -725,11 +1107,103 @@ private:
UINT m_streamStride = 0;
MtCpuIndexBuffer* m_indices = nullptr;
UINT m_baseVertex = 0;
struct GeometryCacheEntry {
std::uint64_t revision;
std::uint64_t last_used;
bool changing;
Render3DDraw geometry;
};
std::unordered_map<std::uint64_t, GeometryCacheEntry> m_geometry_cache;
std::uint64_t m_cache_pruned_frame = 0;
std::vector<float> m_offscreenDepth;
};
}
std::string MtCpuTextureNameFromHandle(const IDirect3DBaseTexture8* handle)
{
if (!handle) return {};
std::lock_guard<std::mutex> lock(g_cpu_tex_mutex);
for (const auto& entry : g_cpu_textures)
{
if (entry.second == handle && !entry.second->levels.empty())
return "mem:cpu_" + std::to_string(entry.first) + "@" + std::to_string(entry.second->revision);
}
return {};
}
bool MtCpuMemoryTexture(const std::string& name, UIMemoryTexture* out)
{
if (name.compare(0, 8, "mem:cpu_") != 0 || !out) return false;
const unsigned id = unsigned(std::strtoul(name.c_str() + 8, nullptr, 10));
std::lock_guard<std::mutex> lock(g_cpu_tex_mutex);
auto it = g_cpu_textures.find(id);
if (it == g_cpu_textures.end() || it->second->levels.empty()) return false;
const auto& lv0 = it->second->levels[0];
const UINT w = lv0.desc.Width;
const UINT h = lv0.desc.Height;
if (!w || !h) return false;
out->width = static_cast<int>(w);
out->height = static_cast<int>(h);
out->revision = it->second->revision;
out->argb.resize(std::size_t(w) * std::size_t(h));
const uint8_t* bytes = lv0.bytes.data();
const int bpp = format_bytes(lv0.desc.Format);
for (std::size_t i = 0; i < out->argb.size(); ++i)
{
if (bpp == 4)
{
std::uint32_t px = 0;
std::memcpy(&px, bytes + i * 4, 4);
// CTerrain::PutImage32 packs alpha into bits 31..24 with RGB == 0.
// Set RGB to white so stage-1 modulation preserves the base texture's RGB.
if ((px & 0x00FFFFFFu) == 0)
px |= 0x00FFFFFFu;
out->argb[i] = px;
}
else if (lv0.desc.Format == D3DFMT_R5G6B5)
{
std::uint16_t word = 0;
std::memcpy(&word, bytes + i * 2, 2);
const std::uint32_t r = ((word >> 11) & 31u) * 255u / 31u;
const std::uint32_t g = ((word >> 5) & 63u) * 255u / 63u;
const std::uint32_t b = (word & 31u) * 255u / 31u;
out->argb[i] = 0xFF000000u | (r << 16) | (g << 8) | b;
}
else if (bpp == 2)
{
std::uint16_t word = 0;
std::memcpy(&word, bytes + i * 2, 2);
// CTerrain::PutImage16 writes `src[x] << 8`, storing the full 8-bit alpha in the high byte.
const std::uint32_t a = (word >> 8) & 0xFFu;
out->argb[i] = (a << 24) | 0x00FFFFFFu;
}
else
{
const std::uint32_t a = bytes[i];
out->argb[i] = (a << 24) | 0x00FFFFFFu;
}
}
return true;
}
IDirect3DDevice8* MtCreateRecordingDevice(int width, int height) { return new RecordingDevice(width, height); }
void SetNativeTerrainRenderEnabled(bool enabled)
{
g_native_terrain_override = enabled ? 1 : 0;
}
bool IsNativeTerrainRenderEnabled()
{
if (g_native_terrain_override >= 0)
return g_native_terrain_override != 0;
static const bool env_on = [] {
const char* v = std::getenv("MT_NATIVE_TERRAIN");
return v && (*v == '1' || *v == 't' || *v == 'T' || *v == 'y' || *v == 'Y');
}();
return env_on;
}
void Render3DBeginFrame()
{
std::lock_guard<std::mutex> lock(g_draws_mutex);
@@ -10,9 +10,14 @@
//
// Matrices are D3D8's row-vector layout (translation in elements 12..14), exactly as 40250 set them.
struct Render3DDraw {
// Stable source-buffer identity and content version. Zero key means an immediate-mode draw
// without a reusable D3D buffer. Geometry may be reused only while both values match.
std::uint64_t geometry_key = 0;
std::uint64_t geometry_revision = 0;
float world[16];
float view[16];
float proj[16];
float viewport[4] = {}; // x, y, width, height
// Stage 0/1 textures, named like UIRenderTextureName: the pack path of a file texture or
// "mem:<id>@<revision>"; empty when the stage has no texture.
@@ -30,6 +35,9 @@ struct Render3DDraw {
std::vector<float> uv1; // u, v
std::vector<std::uint32_t> diffuse; // 0xAARRGGBB
std::vector<std::uint32_t> indices; // triangle list (strips and fans are expanded), or line list
std::vector<std::uint8_t> bone_indices; // 4 uint8 per vertex (mesh-local bone palette index)
std::vector<float> bone_weights; // 4 floats per vertex
std::vector<float> bone_matrices; // 16 floats per bone in the mesh's palette (D3D row-vector layout)
bool lines = false;
// D3DRS_* / D3DTSS_* values in effect for the draw.
@@ -37,8 +45,12 @@ struct Render3DDraw {
std::uint32_t alpha_test = 0, alpha_ref = 0, alpha_func = 0;
std::uint32_t cull_mode = 0, z_enable = 0, z_write = 0, z_func = 0;
std::uint32_t lighting = 0, texture_factor = 0, fog_enable = 0;
std::uint32_t fog_color = 0, fog_vertex_mode = 0, fog_table_mode = 0, fog_range_enable = 0;
float fog_start = 0, fog_end = 0, fog_density = 0;
std::uint32_t color_op[2] = {}, color_arg1[2] = {}, color_arg2[2] = {};
std::uint32_t alpha_op[2] = {}, alpha_arg1[2] = {}, alpha_arg2[2] = {};
std::uint32_t address_u[2] = {}, address_v[2] = {};
std::uint32_t min_filter[2] = {}, mag_filter[2] = {}, mip_filter[2] = {};
// D3DMATERIAL8 diffuse/ambient/emissive (r, g, b, a) and light 0 when enabled.
float material_diffuse[4] = {1, 1, 1, 1};
float material_ambient[4] = {};
@@ -48,8 +60,40 @@ struct Render3DDraw {
float light0_diffuse[4] = {};
float light0_ambient[4] = {};
std::uint32_t ambient = 0; // D3DRS_AMBIENT
struct Light {
std::uint32_t type = 0; // zero when disabled; D3DLIGHT_POINT/SPOT/DIRECTIONAL otherwise
float position[3] = {};
float direction[3] = {};
float diffuse[4] = {};
float ambient[4] = {};
float attenuation[3] = {};
float range = 0;
float theta = 0, phi = 0, falloff = 0;
} lights[2];
std::uint32_t diffuse_material_source = 0;
std::uint32_t ambient_material_source = 0;
std::uint32_t color_vertex = 0;
};
struct GpuSkinSubrangeView {
std::uint64_t source_mesh_key = 0;
std::uint32_t mesh_base_vertex = 0;
std::uint32_t mesh_vertex_count = 0;
const std::uint8_t* bone_indices = nullptr; // mesh_vertex_count * 4
const float* bone_weights = nullptr; // mesh_vertex_count * 4
const float* bone_matrices = nullptr; // bone_count * 16
std::uint32_t bone_count = 0;
};
void SetNativeTerrainRenderEnabled(bool enabled);
bool IsNativeTerrainRenderEnabled();
void SetGpuSkinningEnabled(bool enabled);
bool IsGpuSkinningEnabled();
bool LookupGpuSkinSubrange(const void* vertex_buffer_base, std::uint32_t stride,
std::uint32_t lo_vertex, std::uint32_t hi_vertex,
GpuSkinSubrangeView* out_view);
void Render3DBeginFrame();
void Render3DAdd(Render3DDraw draw);
const std::vector<Render3DDraw>& Render3DDraws();
+666 -78
View File
@@ -1,199 +1,787 @@
// Platform skeleton for EterLib/SkyBox.h (40250 EterLib/SkyBox.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// Platform implementation of EterLib/SkyBox.cpp (40250 EterLib/SkyBox.cpp).
#include "EterLib/StdAfx.h"
#include "EterLib/SkyBox.h"
#include "../PlatformStub.h"
#include "EterLib/Camera.h"
#include "EterLib/StateManager.h"
#include "EterLib/ResourceManager.h"
#include "EterBase/Timer.h"
#include "RenderCommands3D.h"
CSkyObjectQuad::CSkyObjectQuad()
{
MT_PLATFORM_STUB();
m_Indices[0] = 0;
m_Indices[1] = 2;
m_Indices[2] = 1;
m_Indices[3] = 3;
for (unsigned char uci = 0; uci < 4; ++uci)
{
memset(&m_Vertex[uci], 0, sizeof(TPDTVertex));
}
}
CSkyObjectQuad::~CSkyObjectQuad()
{
MT_PLATFORM_STUB();
}
auto CSkyObjectQuad::Clear(const unsigned char &, const float &, const float &, const float &, const float &) -> void
void CSkyObjectQuad::Clear(const unsigned char & c_rucNumVertex,
const float & c_rfRed,
const float & c_rfGreen,
const float & c_rfBlue,
const float & c_rfAlpha)
{
MT_PLATFORM_STUB();
if (c_rucNumVertex > 3)
return;
m_Helper[c_rucNumVertex].Clear(c_rfRed, c_rfGreen, c_rfBlue, c_rfAlpha);
}
auto CSkyObjectQuad::SetSrcColor(const unsigned char &, const float &, const float &, const float &, const float &) -> void
void CSkyObjectQuad::SetSrcColor(const unsigned char & c_rucNumVertex,
const float & c_rfRed,
const float & c_rfGreen,
const float & c_rfBlue,
const float & c_rfAlpha)
{
MT_PLATFORM_STUB();
if (c_rucNumVertex > 3)
return;
m_Helper[c_rucNumVertex].SetSrcColor(c_rfRed, c_rfGreen, c_rfBlue, c_rfAlpha);
}
auto CSkyObjectQuad::SetTransition(const unsigned char &, const float &, const float &, const float &, const float &, DWORD) -> void
void CSkyObjectQuad::SetTransition(const unsigned char & c_rucNumVertex,
const float & c_rfRed,
const float & c_rfGreen,
const float & c_rfBlue,
const float & c_rfAlpha,
DWORD dwDuration)
{
MT_PLATFORM_STUB();
if (c_rucNumVertex > 3)
return;
m_Helper[c_rucNumVertex].SetTransition(c_rfRed, c_rfGreen, c_rfBlue, c_rfAlpha, dwDuration);
}
auto CSkyObjectQuad::SetVertex(const unsigned char &, const TPDTVertex &) -> void
void CSkyObjectQuad::SetVertex(const unsigned char & c_rucNumVertex, const TPDTVertex & c_rPDTVertex)
{
MT_PLATFORM_STUB();
if (c_rucNumVertex > 3)
return;
memcpy(&m_Vertex[m_Indices[c_rucNumVertex]], &c_rPDTVertex, sizeof(TPDTVertex));
}
auto CSkyObjectQuad::StartTransition() -> void
void CSkyObjectQuad::StartTransition()
{
MT_PLATFORM_STUB();
for (unsigned char uci = 0; uci < 4; ++uci)
{
m_Helper[uci].StartTransition();
}
}
auto CSkyObjectQuad::Update() -> bool
bool CSkyObjectQuad::Update()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
bool bResult = false;
for (unsigned char uci = 0; uci < 4; ++uci)
{
bResult = m_Helper[uci].Update() || bResult;
m_Vertex[m_Indices[uci]].diffuse = m_Helper[uci].GetCurColor();
}
return bResult;
}
auto CSkyObjectQuad::Render() -> void
void CSkyObjectQuad::Render()
{
MT_PLATFORM_STUB();
if (CGraphicBase::SetPDTStream(m_Vertex, 4))
STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
}
CSkyObject::CSkyObject()
CSkyObject::CSkyObject() :
m_v3Position(0.0f, 0.0f, 0.0f),
m_fScaleX(1.0f),
m_fScaleY(1.0f),
m_fScaleZ(1.0f)
{
MT_PLATFORM_STUB();
D3DXMatrixIdentity(&m_matWorld);
D3DXMatrixIdentity(&m_matTranslation);
D3DXMatrixIdentity(&m_matWorldCloud);
D3DXMatrixIdentity(&m_matTranslationCloud);
D3DXMatrixIdentity(&m_matTextureCloud);
m_dwlastTime = CTimer::Instance().GetCurrentMillisecond();
m_fCloudPositionU = 0.0f;
m_fCloudPositionV = 0.0f;
m_fCloudScaleX = 1.0f;
m_fCloudScaleY = 1.0f;
m_fCloudHeight = 0.0f;
m_fCloudTextureScaleX = 1.0f;
m_fCloudTextureScaleY = 1.0f;
m_fCloudScrollSpeedU = 0.0f;
m_fCloudScrollSpeedV = 0.0f;
m_ucRenderMode = SKY_RENDER_MODE_DEFAULT;
m_bTransitionStarted = false;
m_bSkyMatrixUpdated = false;
}
CSkyObject::~CSkyObject()
{
MT_PLATFORM_STUB();
}
auto CSkyObject::StartTransition() -> void
void CSkyObject::Destroy()
{
MT_PLATFORM_STUB();
}
auto CSkyObject::GenerateTexture(const char *) -> CGraphicImageInstance *
void CSkyObject::Update()
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<CGraphicImageInstance *>();
CCamera* pCamera = CCameraManager::Instance().GetCurrentCamera();
if (!pCamera)
return;
D3DXVECTOR3 v3Eye = pCamera->GetEye();
if (m_v3Position == v3Eye)
if (m_bSkyMatrixUpdated == false)
return;
m_v3Position = v3Eye;
m_matWorld._41 = m_v3Position.x;
m_matWorld._42 = m_v3Position.y;
m_matWorld._43 = m_v3Position.z;
m_matWorldCloud._41 = m_v3Position.x;
m_matWorldCloud._42 = m_v3Position.y;
m_matWorldCloud._43 = m_v3Position.z + m_fCloudHeight;
if (m_bSkyMatrixUpdated)
m_bSkyMatrixUpdated = false;
}
auto CSkyObject::DeleteTexture(CGraphicImageInstance *) -> void
void CSkyObject::Render()
{
MT_PLATFORM_STUB();
}
auto CSkyObject::CSkyBox::StartTransition() -> void
void CSkyObject::StartTransition()
{
MT_PLATFORM_STUB();
}
auto CSkyObject::CSkyBox::Update() -> bool
CGraphicImageInstance * CSkyObject::GenerateTexture(const char * szfilename)
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
if (!szfilename || !*szfilename)
return NULL;
CResource * pResource = CResourceManager::Instance().GetResourcePointer(szfilename);
if (!pResource || !pResource->IsType(CGraphicImage::Type()))
return NULL;
CGraphicImageInstance * pImageInstance = CGraphicImageInstance::New();
pImageInstance->SetImagePointer(static_cast<CGraphicImage *>(pResource));
return pImageInstance;
}
auto CSkyObject::CSkyBox::Render() -> void
void CSkyObject::DeleteTexture(CGraphicImageInstance * pImageInstance)
{
MT_PLATFORM_STUB();
if (pImageInstance)
CGraphicImageInstance::Delete(pImageInstance);
}
void CSkyObject::TSkyObjectFace::StartTransition()
{
for (unsigned char uci = 0; uci < m_SkyObjectQuadVector.size(); ++uci)
{
m_SkyObjectQuadVector[uci].StartTransition();
}
}
bool CSkyObject::TSkyObjectFace::Update()
{
bool bResult = false;
for (DWORD dwi = 0; dwi < m_SkyObjectQuadVector.size(); ++dwi)
bResult = m_SkyObjectQuadVector[dwi].Update() || bResult;
return bResult;
}
void CSkyObject::TSkyObjectFace::Render()
{
for (unsigned char uci = 0; uci < m_SkyObjectQuadVector.size(); ++uci)
{
m_SkyObjectQuadVector[uci].Render();
}
}
CSkyBox::CSkyBox()
{
MT_PLATFORM_STUB();
m_ucVirticalGradientLevelUpper = 0;
m_ucVirticalGradientLevelLower = 0;
}
CSkyBox::~CSkyBox()
{
MT_PLATFORM_STUB();
Destroy();
}
auto CSkyBox::Update() -> void
void CSkyBox::Destroy()
{
MT_PLATFORM_STUB();
Unload();
}
auto CSkyBox::Render() -> void
void CSkyBox::Unload()
{
MT_PLATFORM_STUB();
TGraphicImageInstanceMap::iterator itor = m_GraphicImageInstanceMap.begin();
while (itor != m_GraphicImageInstanceMap.end())
{
DeleteTexture(itor->second);
++itor;
}
m_GraphicImageInstanceMap.clear();
}
auto CSkyBox::RenderCloud() -> void
void CSkyBox::SetSkyBoxScale(const D3DXVECTOR3 & c_rv3Scale)
{
MT_PLATFORM_STUB();
m_fScaleX = c_rv3Scale.x;
m_fScaleY = c_rv3Scale.y;
m_fScaleZ = c_rv3Scale.z;
m_bSkyMatrixUpdated = true;
D3DXMatrixScaling(&m_matWorld, m_fScaleX, m_fScaleY, m_fScaleZ);
}
auto CSkyBox::Destroy() -> void
void CSkyBox::SetGradientLevel(BYTE byUpper, BYTE byLower)
{
MT_PLATFORM_STUB();
m_ucVirticalGradientLevelUpper = byUpper;
m_ucVirticalGradientLevelLower = byLower;
}
auto CSkyBox::Unload() -> void
void CSkyBox::SetFaceTexture(const char* c_szFileName, int iFaceIndex)
{
MT_PLATFORM_STUB();
if (iFaceIndex < 0 || iFaceIndex > 5 || !c_szFileName || !*c_szFileName)
return;
TGraphicImageInstanceMap::iterator itor = m_GraphicImageInstanceMap.find(c_szFileName);
if (m_GraphicImageInstanceMap.end() != itor)
return;
m_Faces[iFaceIndex].m_strFaceTextureFileName = c_szFileName;
CGraphicImageInstance * pGraphicImageInstance = GenerateTexture(c_szFileName);
m_GraphicImageInstanceMap.insert(TGraphicImageInstanceMap::value_type(c_szFileName, pGraphicImageInstance));
}
auto CSkyBox::SetSkyBoxScale(const D3DXVECTOR3 &) -> void
void CSkyBox::SetCloudTexture(const char * c_szFileName)
{
MT_PLATFORM_STUB();
if (!c_szFileName || !*c_szFileName)
return;
TGraphicImageInstanceMap::iterator itor = m_GraphicImageInstanceMap.find(c_szFileName);
if (m_GraphicImageInstanceMap.end() != itor)
return;
m_FaceCloud.m_strfacename = c_szFileName;
CGraphicImageInstance * pGraphicImageInstance = GenerateTexture(c_szFileName);
m_GraphicImageInstanceMap.insert(TGraphicImageInstanceMap::value_type(m_FaceCloud.m_strfacename, pGraphicImageInstance));
}
auto CSkyBox::SetGradientLevel(BYTE, BYTE) -> void
void CSkyBox::SetCloudScale(const D3DXVECTOR2 & c_rv2CloudScale)
{
MT_PLATFORM_STUB();
m_fCloudScaleX = c_rv2CloudScale.x;
m_fCloudScaleY = c_rv2CloudScale.y;
D3DXMatrixScaling(&m_matWorldCloud, m_fCloudScaleX, m_fCloudScaleY, 1.0f);
}
auto CSkyBox::SetFaceTexture(const char *, int) -> void
void CSkyBox::SetCloudHeight(float fHeight)
{
MT_PLATFORM_STUB();
m_fCloudHeight = fHeight;
}
auto CSkyBox::SetCloudTexture(const char *) -> void
void CSkyBox::SetCloudTextureScale(const D3DXVECTOR2 & c_rv2CloudTextureScale)
{
MT_PLATFORM_STUB();
m_fCloudTextureScaleX = c_rv2CloudTextureScale.x;
m_fCloudTextureScaleY = c_rv2CloudTextureScale.y;
m_matTextureCloud._11 = m_fCloudTextureScaleX;
m_matTextureCloud._22 = m_fCloudTextureScaleY;
}
auto CSkyBox::SetCloudScale(const D3DXVECTOR2 &) -> void
void CSkyBox::SetCloudScrollSpeed(const D3DXVECTOR2 & c_rv2CloudScrollSpeed)
{
MT_PLATFORM_STUB();
m_fCloudScrollSpeedU = c_rv2CloudScrollSpeed.x;
m_fCloudScrollSpeedV = c_rv2CloudScrollSpeed.y;
}
auto CSkyBox::SetCloudHeight(float) -> void
void CSkyBox::SetSkyObjectQuadVertical(TSkyObjectQuadVector * pSkyObjectQuadVector, const D3DXVECTOR2 * c_pv2QuadPoints)
{
MT_PLATFORM_STUB();
TPDTVertex aPDTVertex;
DWORD dwIndex = 0;
pSkyObjectQuadVector->clear();
pSkyObjectQuadVector->resize(m_ucVirticalGradientLevelUpper + m_ucVirticalGradientLevelLower);
unsigned char ucY;
for (ucY = 0; ucY < m_ucVirticalGradientLevelUpper; ++ucY)
{
CSkyObjectQuad & rSkyObjectQuad = pSkyObjectQuadVector->at(dwIndex++);
aPDTVertex.position.x = c_pv2QuadPoints[0].x;
aPDTVertex.position.y = c_pv2QuadPoints[0].y;
aPDTVertex.position.z = 1.0f - (float)(ucY + 1) / (float)(m_ucVirticalGradientLevelUpper);
aPDTVertex.texCoord.x = 0.0f;
aPDTVertex.texCoord.y = (float)(ucY + 1) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(0, aPDTVertex);
aPDTVertex.position.x = c_pv2QuadPoints[0].x;
aPDTVertex.position.y = c_pv2QuadPoints[0].y;
aPDTVertex.position.z = 1.0f - (float)(ucY) / (float)(m_ucVirticalGradientLevelUpper);
aPDTVertex.texCoord.x = 0.0f;
aPDTVertex.texCoord.y = (float)(ucY) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(1, aPDTVertex);
aPDTVertex.position.x = c_pv2QuadPoints[1].x;
aPDTVertex.position.y = c_pv2QuadPoints[1].y;
aPDTVertex.position.z = 1.0f - (float)(ucY + 1) / (float)(m_ucVirticalGradientLevelUpper);
aPDTVertex.texCoord.x = 1.0f;
aPDTVertex.texCoord.y = (float)(ucY + 1) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(2, aPDTVertex);
aPDTVertex.position.x = c_pv2QuadPoints[1].x;
aPDTVertex.position.y = c_pv2QuadPoints[1].y;
aPDTVertex.position.z = 1.0f - (float)(ucY) / (float)(m_ucVirticalGradientLevelUpper);
aPDTVertex.texCoord.x = 1.0f;
aPDTVertex.texCoord.y = (float)(ucY) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(3, aPDTVertex);
}
for (ucY = 0; ucY < m_ucVirticalGradientLevelLower; ++ucY)
{
CSkyObjectQuad & rSkyObjectQuad = pSkyObjectQuadVector->at(dwIndex++);
aPDTVertex.position.x = c_pv2QuadPoints[0].x;
aPDTVertex.position.y = c_pv2QuadPoints[0].y;
aPDTVertex.position.z = -(float)(ucY + 1) / (float)(m_ucVirticalGradientLevelLower);
aPDTVertex.texCoord.x = 0.0f;
aPDTVertex.texCoord.y = 0.5f + (float)(ucY + 1) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(0, aPDTVertex);
aPDTVertex.position.x = c_pv2QuadPoints[0].x;
aPDTVertex.position.y = c_pv2QuadPoints[0].y;
aPDTVertex.position.z = -(float)(ucY) / (float)(m_ucVirticalGradientLevelLower);
aPDTVertex.texCoord.x = 0.0f;
aPDTVertex.texCoord.y = 0.5f + (float)(ucY) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(1, aPDTVertex);
aPDTVertex.position.x = c_pv2QuadPoints[1].x;
aPDTVertex.position.y = c_pv2QuadPoints[1].y;
aPDTVertex.position.z = -(float)(ucY + 1) / (float)(m_ucVirticalGradientLevelLower);
aPDTVertex.texCoord.x = 1.0f;
aPDTVertex.texCoord.y = 0.5f + (float)(ucY + 1) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(2, aPDTVertex);
aPDTVertex.position.x = c_pv2QuadPoints[1].x;
aPDTVertex.position.y = c_pv2QuadPoints[1].y;
aPDTVertex.position.z = -(float)(ucY) / (float)(m_ucVirticalGradientLevelLower);
aPDTVertex.texCoord.x = 1.0f;
aPDTVertex.texCoord.y = 0.5f + (float)(ucY) / (float)(m_ucVirticalGradientLevelUpper) * 0.5f;
rSkyObjectQuad.SetVertex(3, aPDTVertex);
}
}
auto CSkyBox::SetCloudTextureScale(const D3DXVECTOR2 &) -> void
void CSkyBox::SetSkyObjectQuadHorizon(TSkyObjectQuadVector * pSkyObjectQuadVector, const D3DXVECTOR3 * c_pv3QuadPoints)
{
MT_PLATFORM_STUB();
pSkyObjectQuadVector->clear();
pSkyObjectQuadVector->resize(1);
CSkyObjectQuad & rSkyObjectQuad = pSkyObjectQuadVector->at(0);
TPDTVertex aPDTVertex{};
aPDTVertex.position = c_pv3QuadPoints[0];
aPDTVertex.texCoord.x = 0.0f;
aPDTVertex.texCoord.y = 1.0f;
rSkyObjectQuad.SetVertex(0, aPDTVertex);
aPDTVertex.position = c_pv3QuadPoints[1];
aPDTVertex.texCoord.x = 0.0f;
aPDTVertex.texCoord.y = 0.0f;
rSkyObjectQuad.SetVertex(1, aPDTVertex);
aPDTVertex.position = c_pv3QuadPoints[2];
aPDTVertex.texCoord.x = 1.0f;
aPDTVertex.texCoord.y = 1.0f;
rSkyObjectQuad.SetVertex(2, aPDTVertex);
aPDTVertex.position = c_pv3QuadPoints[3];
aPDTVertex.texCoord.x = 1.0f;
aPDTVertex.texCoord.y = 0.0f;
rSkyObjectQuad.SetVertex(3, aPDTVertex);
}
auto CSkyBox::SetCloudScrollSpeed(const D3DXVECTOR2 &) -> void
void CSkyBox::Refresh()
{
MT_PLATFORM_STUB();
D3DXVECTOR3 v3QuadPoints[4];
if (m_ucRenderMode == CSkyObject::SKY_RENDER_MODE_DEFAULT || m_ucRenderMode == CSkyObject::SKY_RENDER_MODE_DIFFUSE)
{
if (m_ucVirticalGradientLevelUpper + m_ucVirticalGradientLevelLower <= 0)
return;
D3DXVECTOR2 v2QuadPoints[2];
v2QuadPoints[0] = D3DXVECTOR2(1.0f, -1.0f);
v2QuadPoints[1] = D3DXVECTOR2(-1.0f, -1.0f);
SetSkyObjectQuadVertical(&m_Faces[0].m_SkyObjectQuadVector, v2QuadPoints);
m_Faces[0].m_strfacename = "front";
v2QuadPoints[0] = D3DXVECTOR2(-1.0f, 1.0f);
v2QuadPoints[1] = D3DXVECTOR2(1.0f, 1.0f);
SetSkyObjectQuadVertical(&m_Faces[1].m_SkyObjectQuadVector, v2QuadPoints);
m_Faces[1].m_strfacename = "back";
v2QuadPoints[0] = D3DXVECTOR2(-1.0f, -1.0f);
v2QuadPoints[1] = D3DXVECTOR2(-1.0f, 1.0f);
SetSkyObjectQuadVertical(&m_Faces[2].m_SkyObjectQuadVector, v2QuadPoints);
m_Faces[2].m_strfacename = "left";
v2QuadPoints[0] = D3DXVECTOR2(1.0f, 1.0f);
v2QuadPoints[1] = D3DXVECTOR2(1.0f, -1.0f);
SetSkyObjectQuadVertical(&m_Faces[3].m_SkyObjectQuadVector, v2QuadPoints);
m_Faces[3].m_strfacename = "right";
v3QuadPoints[0] = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
v3QuadPoints[1] = D3DXVECTOR3(-1.0f, 1.0f, 1.0f);
v3QuadPoints[2] = D3DXVECTOR3(1.0f, -1.0f, 1.0f);
v3QuadPoints[3] = D3DXVECTOR3(-1.0f, -1.0f, 1.0f);
SetSkyObjectQuadHorizon(&m_Faces[4].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[4].m_strfacename = "top";
v3QuadPoints[0] = D3DXVECTOR3(-1.0f, 1.0f, -1.0f);
v3QuadPoints[1] = D3DXVECTOR3(1.0f, 1.0f, -1.0f);
v3QuadPoints[2] = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
v3QuadPoints[3] = D3DXVECTOR3(1.0f, -1.0f, -1.0f);
SetSkyObjectQuadHorizon(&m_Faces[5].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[5].m_strfacename = "bottom";
}
else if (m_ucRenderMode == CSkyObject::SKY_RENDER_MODE_TEXTURE)
{
v3QuadPoints[0] = D3DXVECTOR3(1.0f, -1.0f, -1.0f);
v3QuadPoints[1] = D3DXVECTOR3(1.0f, -1.0f, 1.0f);
v3QuadPoints[2] = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
v3QuadPoints[3] = D3DXVECTOR3(-1.0f, -1.0f, 1.0f);
SetSkyObjectQuadHorizon(&m_Faces[0].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[0].m_strfacename = "front";
v3QuadPoints[0] = D3DXVECTOR3(-1.0f, 1.0f, -1.0f);
v3QuadPoints[1] = D3DXVECTOR3(-1.0f, 1.0f, 1.0f);
v3QuadPoints[2] = D3DXVECTOR3(1.0f, 1.0f, -1.0f);
v3QuadPoints[3] = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
SetSkyObjectQuadHorizon(&m_Faces[1].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[1].m_strfacename = "back";
v3QuadPoints[0] = D3DXVECTOR3(1.0f, 1.0f, -1.0f);
v3QuadPoints[1] = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
v3QuadPoints[2] = D3DXVECTOR3(1.0f, -1.0f, -1.0f);
v3QuadPoints[3] = D3DXVECTOR3(1.0f, -1.0f, 1.0f);
SetSkyObjectQuadHorizon(&m_Faces[2].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[2].m_strfacename = "left";
v3QuadPoints[0] = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
v3QuadPoints[1] = D3DXVECTOR3(-1.0f, -1.0f, 1.0f);
v3QuadPoints[2] = D3DXVECTOR3(-1.0f, 1.0f, -1.0f);
v3QuadPoints[3] = D3DXVECTOR3(-1.0f, 1.0f, 1.0f);
SetSkyObjectQuadHorizon(&m_Faces[3].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[3].m_strfacename = "right";
v3QuadPoints[0] = D3DXVECTOR3(1.0f, -1.0f, 1.0f);
v3QuadPoints[1] = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
v3QuadPoints[2] = D3DXVECTOR3(-1.0f, -1.0f, 1.0f);
v3QuadPoints[3] = D3DXVECTOR3(-1.0f, 1.0f, 1.0f);
SetSkyObjectQuadHorizon(&m_Faces[4].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[4].m_strfacename = "top";
v3QuadPoints[0] = D3DXVECTOR3(1.0f, -1.0f, -1.0f);
v3QuadPoints[1] = D3DXVECTOR3(1.0f, 1.0f, -1.0f);
v3QuadPoints[2] = D3DXVECTOR3(-1.0f, -1.0f, -1.0f);
v3QuadPoints[3] = D3DXVECTOR3(-1.0f, 1.0f, -1.0f);
SetSkyObjectQuadHorizon(&m_Faces[5].m_SkyObjectQuadVector, v3QuadPoints);
m_Faces[5].m_strfacename = "bottom";
}
v3QuadPoints[0] = D3DXVECTOR3(1.0f, 1.0f, 0.0f);
v3QuadPoints[1] = D3DXVECTOR3(-1.0f, 1.0f, 0.0f);
v3QuadPoints[2] = D3DXVECTOR3(1.0f, -1.0f, 0.0f);
v3QuadPoints[3] = D3DXVECTOR3(-1.0f, -1.0f, 0.0f);
SetSkyObjectQuadHorizon(&m_FaceCloud.m_SkyObjectQuadVector, v3QuadPoints);
}
auto CSkyBox::SetCloudColor(const TGradientColor &, const TGradientColor &, const DWORD &) -> void
void CSkyBox::SetCloudColor(const TGradientColor & c_rColor, const TGradientColor & c_rNextColor, const DWORD & dwTransitionTime)
{
MT_PLATFORM_STUB();
TSkyObjectFace & aFaceCloud = m_FaceCloud;
for (DWORD dwk = 0; dwk < aFaceCloud.m_SkyObjectQuadVector.size(); ++dwk)
{
CSkyObjectQuad & aSkyObjectQuad = aFaceCloud.m_SkyObjectQuadVector[dwk];
for (unsigned char v = 0; v < 4; ++v)
{
aSkyObjectQuad.SetSrcColor(v,
c_rColor.m_FirstColor.r,
c_rColor.m_FirstColor.g,
c_rColor.m_FirstColor.b,
c_rColor.m_FirstColor.a);
aSkyObjectQuad.SetTransition(v,
c_rNextColor.m_FirstColor.r,
c_rNextColor.m_FirstColor.g,
c_rNextColor.m_FirstColor.b,
c_rNextColor.m_FirstColor.a,
dwTransitionTime);
}
}
}
auto CSkyBox::Refresh() -> void
void CSkyBox::SetSkyColor(const TVectorGradientColor & c_rColorVector, const TVectorGradientColor & c_rNextColorVector, long lTransitionTime)
{
MT_PLATFORM_STUB();
if (c_rColorVector.empty() || c_rNextColorVector.empty())
return;
unsigned long ulVectorGradientColornum = 0;
unsigned long uck;
for (unsigned char ucj = 0; ucj < 4; ++ucj)
{
TSkyObjectFace & aFace = m_Faces[ucj];
ulVectorGradientColornum = 0;
for (uck = 0; uck < aFace.m_SkyObjectQuadVector.size(); ++uck)
{
if (ulVectorGradientColornum >= c_rColorVector.size() || ulVectorGradientColornum >= c_rNextColorVector.size())
break;
CSkyObjectQuad & aSkyObjectQuad = aFace.m_SkyObjectQuadVector[uck];
aSkyObjectQuad.SetSrcColor(0,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.r,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.g,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.b,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.a);
aSkyObjectQuad.SetTransition(0,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.r,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.g,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.b,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.a,
lTransitionTime);
aSkyObjectQuad.SetSrcColor(1,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.r,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.g,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.b,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.a);
aSkyObjectQuad.SetTransition(1,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.r,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.g,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.b,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.a,
lTransitionTime);
aSkyObjectQuad.SetSrcColor(2,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.r,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.g,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.b,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.a);
aSkyObjectQuad.SetTransition(2,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.r,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.g,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.b,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.a,
lTransitionTime);
aSkyObjectQuad.SetSrcColor(3,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.r,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.g,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.b,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.a);
aSkyObjectQuad.SetTransition(3,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.r,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.g,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.b,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.a,
lTransitionTime);
ulVectorGradientColornum++;
}
}
TSkyObjectFace & aFaceTop = m_Faces[4];
ulVectorGradientColornum = 0;
for (uck = 0; uck < aFaceTop.m_SkyObjectQuadVector.size(); ++uck)
{
CSkyObjectQuad & aSkyObjectQuad = aFaceTop.m_SkyObjectQuadVector[uck];
for (unsigned char v = 0; v < 4; ++v)
{
aSkyObjectQuad.SetSrcColor(v,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.r,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.g,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.b,
c_rColorVector[ulVectorGradientColornum].m_FirstColor.a);
aSkyObjectQuad.SetTransition(v,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.r,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.g,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.b,
c_rNextColorVector[ulVectorGradientColornum].m_FirstColor.a,
lTransitionTime);
}
}
TSkyObjectFace & aFaceBottom = m_Faces[5];
ulVectorGradientColornum = c_rColorVector.size() - 1;
for (uck = 0; uck < aFaceBottom.m_SkyObjectQuadVector.size(); ++uck)
{
CSkyObjectQuad & aSkyObjectQuad = aFaceBottom.m_SkyObjectQuadVector[uck];
for (unsigned char v = 0; v < 4; ++v)
{
aSkyObjectQuad.SetSrcColor(v,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.r,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.g,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.b,
c_rColorVector[ulVectorGradientColornum].m_SecondColor.a);
aSkyObjectQuad.SetTransition(v,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.r,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.g,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.b,
c_rNextColorVector[ulVectorGradientColornum].m_SecondColor.a,
lTransitionTime);
}
}
}
auto CSkyBox::SetSkyColor(const TVectorGradientColor &, const TVectorGradientColor &, long) -> void
void CSkyBox::StartTransition()
{
MT_PLATFORM_STUB();
m_bTransitionStarted = true;
for (unsigned char ucj = 0; ucj < 6; ++ucj)
m_Faces[ucj].StartTransition();
m_FaceCloud.StartTransition();
}
auto CSkyBox::StartTransition() -> void
void CSkyBox::Update()
{
MT_PLATFORM_STUB();
CSkyObject::Update();
if (!m_bTransitionStarted)
return;
bool bResult = false;
for (unsigned char uci = 0; uci < 6; ++uci)
bResult = m_Faces[uci].Update() || bResult;
bResult = m_FaceCloud.Update() || bResult;
m_bTransitionStarted = bResult;
}
auto CSkyBox::SetSkyObjectQuadVertical(TSkyObjectQuadVector *, const D3DXVECTOR2 *) -> void
void CSkyBox::Render()
{
MT_PLATFORM_STUB();
if (!IsNativeTerrainRenderEnabled())
return;
STATEMANAGER.SaveRenderState(D3DRS_ZENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_FOGENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SetVertexShader(D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1);
STATEMANAGER.SetTransform(D3DTS_WORLD, &m_matWorld);
if (m_ucRenderMode == CSkyObject::SKY_RENDER_MODE_TEXTURE)
{
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
for (unsigned int i = 0; i < 6; ++i)
{
CGraphicImageInstance * pFaceImageInstance = m_GraphicImageInstanceMap[m_Faces[i].m_strFaceTextureFileName];
if (!pFaceImageInstance)
break;
STATEMANAGER.SetTexture(0, pFaceImageInstance->GetTextureReference().GetD3DTexture());
m_Faces[i].Render();
}
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSU);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSV);
}
else
{
STATEMANAGER.SetTexture(0, NULL);
for (unsigned int i = 0; i < 6; ++i)
{
m_Faces[i].Render();
}
}
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING);
STATEMANAGER.RestoreRenderState(D3DRS_ZENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_FOGENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLOROP);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG1);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG2);
}
auto CSkyBox::SetSkyObjectQuadHorizon(TSkyObjectQuadVector *, const D3DXVECTOR3 *) -> void
void CSkyBox::RenderCloud()
{
MT_PLATFORM_STUB();
if (!IsNativeTerrainRenderEnabled())
return;
CGraphicImageInstance * pCloudGraphicImageInstance = m_GraphicImageInstanceMap[m_FaceCloud.m_strfacename];
if (!pCloudGraphicImageInstance || pCloudGraphicImageInstance->IsEmpty() || !pCloudGraphicImageInstance->GetTexturePointer())
return;
STATEMANAGER.SaveRenderState(D3DRS_ZENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_FOGENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
STATEMANAGER.SaveRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCCOLOR);
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
m_matTextureCloud._31 = m_fCloudPositionU;
m_matTextureCloud._32 = m_fCloudPositionV;
DWORD dwCurTime = CTimer::Instance().GetCurrentMillisecond();
m_fCloudPositionU += m_fCloudScrollSpeedU * (float)(dwCurTime - m_dwlastTime) * 0.001f;
if (m_fCloudPositionU >= 1.0f)
m_fCloudPositionU = 0.0f;
m_fCloudPositionV += m_fCloudScrollSpeedV * (float)(dwCurTime - m_dwlastTime) * 0.001f;
if (m_fCloudPositionV >= 1.0f)
m_fCloudPositionV = 0.0f;
m_dwlastTime = dwCurTime;
STATEMANAGER.SaveTransform(D3DTS_TEXTURE0, &m_matTextureCloud);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATEINVALPHA_ADDCOLOR);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
D3DXMATRIX matProjCloud;
D3DXMatrixPerspectiveFovRH(&matProjCloud, D3DX_PI * 0.25f, 1.33333f, 50.0f, 999999.0f);
STATEMANAGER.SetTransform(D3DTS_WORLD, &m_matWorldCloud);
STATEMANAGER.SaveTransform(D3DTS_PROJECTION, &matProjCloud);
STATEMANAGER.SetTexture(0, pCloudGraphicImageInstance->GetTexturePointer()->GetD3DTexture());
m_FaceCloud.Render();
STATEMANAGER.RestoreTransform(D3DTS_PROJECTION);
STATEMANAGER.RestoreTransform(D3DTS_TEXTURE0);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS);
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING);
STATEMANAGER.RestoreRenderState(D3DRS_ZENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_FOGENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_SRCBLEND);
STATEMANAGER.RestoreRenderState(D3DRS_DESTBLEND);
}
@@ -1,11 +1,13 @@
#include "EterLib/StdAfx.h"
#include "UIRenderCommands.h"
#include "RenderCommands3D.h"
#include "EterLib/StateManager.h"
#include <algorithm>
namespace {
std::vector<UIRenderCommand> commands;
std::uint64_t frame_id = 0;
int canvas_width = 0;
int canvas_height = 0;
float clip_x1 = 0, clip_y1 = 0, clip_x2 = 0, clip_y2 = 0;
@@ -18,14 +20,17 @@ void UIRenderGetSize(unsigned* width, unsigned* height) {
if (height) *height = canvas_height;
}
void UIRenderBeginFrame() {
++frame_id;
commands.clear();
clip_x1 = clip_y1 = 0;
clip_x2 = canvas_width;
clip_y2 = canvas_height;
}
std::uint64_t UIRenderFrameId() { return frame_id; }
void UIRenderAdd(UIRenderCommand command) {
command.clip_x1 = clip_x1; command.clip_y1 = clip_y1;
command.clip_x2 = clip_x2; command.clip_y2 = clip_y2;
command.behind_3d = Render3DDraws().empty();
commands.push_back(command);
}
const std::vector<UIRenderCommand>& UIRenderCommands() { return commands; }
@@ -23,6 +23,7 @@ struct UIRenderCommand {
// circular minimap_image_filter): the mask's texture coordinates at the quad's four corners.
std::string mask;
float mu[4] = {}, mv[4] = {};
bool behind_3d = false;
};
// D3DXCOLOR (0..1 floats) -> the 0xAARRGGBB the commands carry.
@@ -54,6 +55,8 @@ struct IDirect3DBaseTexture8;
void UIRenderReleaseMemoryTexture(IDirect3DTexture8* texture);
// The name (as UIRenderTextureName) of the texture a D3D handle belongs to; "" for null or unknown.
std::string UIRenderTextureNameFromHandle(const IDirect3DBaseTexture8* handle);
std::string MtCpuTextureNameFromHandle(const IDirect3DBaseTexture8* handle);
bool MtCpuMemoryTexture(const std::string& name, UIMemoryTexture* out);
// A textured quad from 40250's TPDTVertex[4] (TL, TR, BL, BR) positions and texture coordinates.
// PORT: D3D8 puts pixel centres on integers, which is why 40250 subtracts 0.5 from every vertex; the
@@ -64,6 +67,7 @@ void UIRenderAddImage(const CGraphicTexture* texture, const float x[4], const fl
void UIRenderSetSize(int width, int height);
void UIRenderGetSize(unsigned* width, unsigned* height);
void UIRenderBeginFrame();
std::uint64_t UIRenderFrameId();
void UIRenderAdd(UIRenderCommand command);
const std::vector<UIRenderCommand>& UIRenderCommands();
void UIRenderSetClip(float x, float y, float width, float height);
@@ -78,11 +78,19 @@ LPDIRECT3D8 CPythonGraphic::GetD3D()
void CPythonGraphic::SetViewport(float x, float y, float width, float height)
{
if (ms_lpd3dDevice)
{
ms_lpd3dDevice->GetViewport(&m_backupViewport);
D3DVIEWPORT8 vp = { (DWORD)x, (DWORD)y, (DWORD)width, (DWORD)height, 0.0f, 1.0f };
ms_lpd3dDevice->SetViewport(&vp);
}
UIRenderSetClip(x, y, width, height);
}
void CPythonGraphic::RestoreViewport()
{
if (ms_lpd3dDevice)
ms_lpd3dDevice->SetViewport(&m_backupViewport);
UIRenderRestoreClip();
}
void CPythonGraphic::SetOmniLight()
@@ -1,7 +1,4 @@
// Platform implementation of 40250 GameLib/MapOutdoorCharacterShadow.cpp. The render-target lifecycle is
// the 40250 code as is; the shadow-map pass is not run (BeginRenderCharacterShadowToTexture reports it
// cannot render) because the terrain that samples the map is drawn by Godot, which casts its own
// character shadows (PORT-PLAN §3).
// Platform implementation of 40250 GameLib/MapOutdoorCharacterShadow.cpp.
#include "GameLib/StdAfx.h"
#include "EterLib/StateManager.h"
#include "EterLib/Camera.h"
@@ -66,14 +63,63 @@ void CMapOutdoor::ReleaseCharacterShadowTexture()
SAFE_RELEASE(m_lpCharacterShadowMapTexture);
}
// PORT: no shadow-map pass (see the file comment); CPythonBackground::RenderCharacterShadowToTexture
// then skips the shadow instances.
static DWORD dwLightEnable = FALSE;
static bool shadowPassBegun = false;
bool CMapOutdoor::BeginRenderCharacterShadowToTexture()
{
return false;
CCamera* camera = CCameraManager::Instance().GetCurrentCamera();
if (!camera)
return false;
if (recreate)
{
CreateCharacterShadowTexture();
recreate = false;
}
if (!m_lpCharacterShadowMapRenderTargetSurface || !m_lpCharacterShadowMapDepthSurface)
return false;
shadowPassBegun = true;
const D3DXVECTOR3 target = camera->GetTarget();
const D3DXVECTOR3 eye(target.x - 1.732f * 1250.0f,
target.y - 1250.0f, target.z + 2.0f * 1.732f * 1250.0f);
const D3DXVECTOR3 up(0.0f, 0.0f, 1.0f);
D3DXMATRIX lightView, lightProj;
D3DXMatrixLookAtRH(&lightView, &eye, &target, &up);
D3DXMatrixOrthoRH(&lightProj, 2550.0f, 2550.0f, 1.0f, 15000.0f);
STATEMANAGER.SaveTransform(D3DTS_VIEW, &lightView);
STATEMANAGER.SaveTransform(D3DTS_PROJECTION, &lightProj);
dwLightEnable = STATEMANAGER.GetRenderState(D3DRS_LIGHTING);
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_TEXTUREFACTOR, 0xFF808080);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
bool success = SUCCEEDED(ms_lpd3dDevice->GetRenderTarget(&m_lpBackupRenderTargetSurface));
success = SUCCEEDED(ms_lpd3dDevice->GetDepthStencilSurface(&m_lpBackupDepthSurface)) && success;
success = SUCCEEDED(ms_lpd3dDevice->SetRenderTarget(m_lpCharacterShadowMapRenderTargetSurface,
m_lpCharacterShadowMapDepthSurface)) && success;
success = SUCCEEDED(ms_lpd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
0xFFFFFFFFu, 1.0f, 0)) && success;
success = SUCCEEDED(ms_lpd3dDevice->GetViewport(&m_BackupViewport)) && success;
success = SUCCEEDED(ms_lpd3dDevice->SetViewport(&m_ShadowMapViewport)) && success;
return success;
}
// PORT: nothing to restore when the pass did not begin.
void CMapOutdoor::EndRenderCharacterShadowToTexture()
{
if (!shadowPassBegun)
return;
shadowPassBegun = false;
ms_lpd3dDevice->SetViewport(&m_BackupViewport);
if (m_lpBackupRenderTargetSurface && m_lpBackupDepthSurface)
ms_lpd3dDevice->SetRenderTarget(m_lpBackupRenderTargetSurface, m_lpBackupDepthSurface);
SAFE_RELEASE(m_lpBackupRenderTargetSurface);
SAFE_RELEASE(m_lpBackupDepthSurface);
STATEMANAGER.RestoreTransform(D3DTS_VIEW);
STATEMANAGER.RestoreTransform(D3DTS_PROJECTION);
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, dwLightEnable);
STATEMANAGER.RestoreRenderState(D3DRS_TEXTUREFACTOR);
}
@@ -1,10 +1,315 @@
// Platform implementation of 40250 GameLib/MapOutdoorRenderHTP.cpp (hardware-transform terrain patches).
// PORT: the Godot Metin2World adapter draws the terrain from the same .raw/.tga/tile data (PORT-PLAN §3):
// the recorder has no texture-coordinate generation or texture transforms, so the patch splat passes are
// not replayed through the D3D8 device.
// PORT: the Godot Metin2World adapter draws the terrain from the same .raw/.tga/tile data (PORT-PLAN §3).
// When IsNativeTerrainRenderEnabled() is true (MT_NATIVE_TERRAIN=1 or standalone native renderer),
// terrain patches are emitted through CStateManager with D3DTSS_TCI_CAMERASPACEPOSITION texture transforms.
#include "GameLib/StdAfx.h"
#include "GameLib/MapOutdoor.h"
#include "GameLib/TerrainPatch.h"
#include "GameLib/AreaTerrain.h"
#include "EterLib/StateManager.h"
#include "../EterLib/RenderCommands3D.h"
#include <algorithm>
void CMapOutdoor::__RenderTerrain_RenderHardwareTransformPatch()
{
if (!IsNativeTerrainRenderEnabled())
return;
const DWORD fogColor = mc_pEnvironmentData ? DWORD(mc_pEnvironmentData->FogColor) : 0xffffffff;
const float fogNear = mc_pEnvironmentData ? mc_pEnvironmentData->GetFogNearDistance() : 5000.0f;
const float fogFar = mc_pEnvironmentData ? mc_pEnvironmentData->GetFogFarDistance() : 10000.0f;
m_matWorldForCommonUse._41 = 0.0f;
m_matWorldForCommonUse._42 = 0.0f;
STATEMANAGER.SetTransform(D3DTS_WORLD, &m_matWorldForCommonUse);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
STATEMANAGER.SaveTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHAREF, 0);
STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
STATEMANAGER.SaveRenderState(D3DRS_TEXTUREFACTOR, fogColor);
STATEMANAGER.SaveRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
STATEMANAGER.SaveRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, TRUE);
STATEMANAGER.SetVertexShader(D3DFVF_XYZ | D3DFVF_NORMAL);
m_iRenderedSplatNumSqSum = 0;
m_iRenderedPatchNum = 0;
m_iRenderedSplatNum = 0;
m_RenderedTextureNumVector.clear();
WORD wPrimitiveCount = 0;
D3DPRIMITIVETYPE ePrimitiveType = D3DPT_TRIANGLESTRIP;
SelectIndexBuffer(0, &wPrimitiveCount, &ePrimitiveType);
const auto nearIt = std::upper_bound(m_PatchVector.begin(), m_PatchVector.end(),
std::pair<float, long>(fogNear - 3200.0f, 0));
const auto farIt = std::upper_bound(m_PatchVector.begin(), m_PatchVector.end(),
std::pair<float, long>(fogFar + 1600.0f, 0));
const float lod1 = __GetNoFogDistance();
const float lod2 = __GetFogDistance();
BYTE lod = 0;
auto updateLod = [&](float distance) {
if (lod == 0 && lod1 <= distance) {
lod = 1;
SelectIndexBuffer(1, &wPrimitiveCount, &ePrimitiveType);
} else if (lod == 1 && lod2 <= distance) {
lod = 2;
SelectIndexBuffer(2, &wPrimitiveCount, &ePrimitiveType);
}
};
const DWORD fogEnabled = STATEMANAGER.GetRenderState(D3DRS_FOGENABLE);
STATEMANAGER.SetRenderState(D3DRS_FOGENABLE, FALSE);
for (auto it = m_PatchVector.begin(); it != nearIt; ++it) {
updateLod(it->first);
__HardwareTransformPatch_RenderPatchSplat(it->second, wPrimitiveCount, ePrimitiveType);
if (m_iRenderedSplatNum >= m_iSplatLimit) break;
if (m_bDrawWireFrame) DrawWireFrame(it->second, wPrimitiveCount, ePrimitiveType);
}
STATEMANAGER.SetRenderState(D3DRS_FOGENABLE, fogEnabled);
if (m_iRenderedSplatNum < m_iSplatLimit) {
for (auto it = nearIt; it != farIt; ++it) {
updateLod(it->first);
__HardwareTransformPatch_RenderPatchSplat(it->second, wPrimitiveCount, ePrimitiveType);
if (m_iRenderedSplatNum >= m_iSplatLimit) break;
if (m_bDrawWireFrame) DrawWireFrame(it->second, wPrimitiveCount, ePrimitiveType);
}
}
STATEMANAGER.SetRenderState(D3DRS_FOGENABLE, FALSE);
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE);
STATEMANAGER.SetTexture(0, NULL);
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, FALSE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, FALSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TFACTOR);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
if (m_iRenderedSplatNum < m_iSplatLimit) {
for (auto it = farIt; it != m_PatchVector.end(); ++it) {
updateLod(it->first);
__HardwareTransformPatch_RenderPatchNone(it->second, wPrimitiveCount, ePrimitiveType);
if (m_iRenderedSplatNum >= m_iSplatLimit) break;
if (m_bDrawWireFrame) DrawWireFrame(it->second, wPrimitiveCount, ePrimitiveType);
}
}
STATEMANAGER.SetRenderState(D3DRS_FOGENABLE, fogEnabled);
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, TRUE);
std::sort(m_RenderedTextureNumVector.begin(), m_RenderedTextureNumVector.end());
m_matWorldForCommonUse._41 = 0.0f;
m_matWorldForCommonUse._42 = 0.0f;
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_DESTBLEND);
STATEMANAGER.RestoreRenderState(D3DRS_SRCBLEND);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHAREF);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC);
STATEMANAGER.RestoreRenderState(D3DRS_TEXTUREFACTOR);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_ADDRESSV);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_ADDRESSU);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_ALPHAOP);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_ALPHAARG1);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_COLOROP);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_COLORARG1);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS);
STATEMANAGER.RestoreTextureStageState(1, D3DTSS_TEXCOORDINDEX);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSV);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSU);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAOP);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ALPHAARG1);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLOROP);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG2);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_COLORARG1);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXCOORDINDEX);
}
void CMapOutdoor::__HardwareTransformPatch_RenderPatchNone(long patchnum, WORD wPrimitiveCount, D3DPRIMITIVETYPE ePrimitiveType)
{
assert(NULL != m_pTerrainPatchProxyList);
CTerrainPatchProxy* patch = &m_pTerrainPatchProxyList[patchnum];
if (!patch->isUsed()) return;
CGraphicVertexBuffer* vb = patch->HardwareTransformPatch_GetVertexBufferPtr();
if (!vb) return;
STATEMANAGER.SetStreamSource(0, vb->GetD3DVertexBuffer(), m_iPatchTerrainVertexSize);
STATEMANAGER.DrawIndexedPrimitive(ePrimitiveType, 0, m_iPatchTerrainVertexCount, 0, wPrimitiveCount);
}
void CMapOutdoor::__HardwareTransformPatch_RenderPatchSplat(long patchnum, WORD wPrimitiveCount, D3DPRIMITIVETYPE ePrimitiveType)
{
assert(NULL != m_pTerrainPatchProxyList && "__HardwareTransformPatch_RenderPatchSplat");
CTerrainPatchProxy * pTerrainPatchProxy = &m_pTerrainPatchProxyList[patchnum];
if (!pTerrainPatchProxy->isUsed())
return;
long sPatchNum = pTerrainPatchProxy->GetPatchNum();
if (sPatchNum < 0)
return;
BYTE ucTerrainNum = pTerrainPatchProxy->GetTerrainNum();
if (0xFF == ucTerrainNum)
return;
CTerrain * pTerrain;
if (!GetTerrainPointer(ucTerrainNum, &pTerrain))
return;
CGraphicVertexBuffer* pkVB = pTerrainPatchProxy->HardwareTransformPatch_GetVertexBufferPtr();
if (!pkVB)
return;
WORD wCoordX, wCoordY;
pTerrain->GetCoordinate(&wCoordX, &wCoordY);
m_matWorldForCommonUse._41 = -(float)(wCoordX * CTerrainImpl::XSIZE * CTerrainImpl::CELLSCALE);
m_matWorldForCommonUse._42 = (float)(wCoordY * CTerrainImpl::YSIZE * CTerrainImpl::CELLSCALE);
D3DXMATRIX matTerrainTexTransform, matSplatAlphaTexTransform;
D3DXMatrixMultiply(&matTerrainTexTransform, &m_matViewInverse, &m_matWorldForCommonUse);
D3DXMatrixMultiply(&matSplatAlphaTexTransform, &matTerrainTexTransform, &m_matSplatAlpha);
STATEMANAGER.SetTransform(D3DTS_TEXTURE1, &matSplatAlphaTexTransform);
STATEMANAGER.SetStreamSource(0, pkVB->GetD3DVertexBuffer(), m_iPatchTerrainVertexSize);
// 40250 renders the terrain splats unlit; only its shadow pass enables lighting.
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE);
TTerrainSplatPatch & rTerrainSplatPatch = pTerrain->GetTerrainSplatPatch();
const DWORD texCount = m_TextureSet.GetTextureCount();
bool isFirst = true;
int renderedSplatCount = 0;
for (DWORD j = 1; j < texCount; ++j)
{
TTerainSplat & rSplat = rTerrainSplatPatch.Splats[j];
if (!rSplat.Active || rTerrainSplatPatch.PatchTileCount[sPatchNum][j] == 0)
continue;
const TTerrainTexture & rTexture = m_TextureSet.GetTexture(j);
if (!rTexture.pd3dTexture)
continue;
D3DXMATRIX matTexTransform;
D3DXMatrixMultiply(&matTexTransform, &m_matViewInverse, &rTexture.m_matTransform);
STATEMANAGER.SetTransform(D3DTS_TEXTURE0, &matTexTransform);
STATEMANAGER.SetTexture(0, rTexture.pd3dTexture);
if (isFirst || !rSplat.pd3dTexture)
{
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
STATEMANAGER.SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
isFirst = false;
}
else
{
STATEMANAGER.SetTexture(1, rSplat.pd3dTexture);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
STATEMANAGER.SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
}
STATEMANAGER.DrawIndexedPrimitive(ePrimitiveType, 0, m_iPatchTerrainVertexCount, 0, wPrimitiveCount);
++renderedSplatCount;
++m_iRenderedSplatNum;
if (std::find(m_RenderedTextureNumVector.begin(), m_RenderedTextureNumVector.end(), int(j)) == m_RenderedTextureNumVector.end())
m_RenderedTextureNumVector.push_back(int(j));
if (m_iRenderedSplatNum >= m_iSplatLimit)
break;
}
if (renderedSplatCount == 0 && texCount > 1)
{
const TTerrainTexture & rTexture = m_TextureSet.GetTexture(1);
if (rTexture.pd3dTexture)
{
D3DXMATRIX matTexTransform;
D3DXMatrixMultiply(&matTexTransform, &m_matViewInverse, &rTexture.m_matTransform);
STATEMANAGER.SetTransform(D3DTS_TEXTURE0, &matTexTransform);
STATEMANAGER.SetTexture(0, rTexture.pd3dTexture);
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
STATEMANAGER.SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
STATEMANAGER.DrawIndexedPrimitive(ePrimitiveType, 0, m_iPatchTerrainVertexCount, 0, wPrimitiveCount);
renderedSplatCount = 1;
++m_iRenderedSplatNum;
}
}
if (m_bDrawShadow && pTerrain->GetShadowTexture())
{
const DWORD previousFogColor = STATEMANAGER.GetRenderState(D3DRS_FOGCOLOR);
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, TRUE);
STATEMANAGER.SetRenderState(D3DRS_FOGCOLOR, 0xFFFFFFFF);
D3DXMATRIX matShadowTexTransform;
D3DXMatrixMultiply(&matShadowTexTransform, &matTerrainTexTransform, &m_matStaticShadow);
STATEMANAGER.SetTransform(D3DTS_TEXTURE0, &matShadowTexTransform);
STATEMANAGER.SetTexture(0, pTerrain->GetShadowTexture());
STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
if (m_bDrawChrShadow && m_lpCharacterShadowMapTexture)
{
STATEMANAGER.SetTransform(D3DTS_TEXTURE1, &m_matDynamicShadow);
STATEMANAGER.SetTexture(1, m_lpCharacterShadowMapTexture);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
}
STATEMANAGER.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
STATEMANAGER.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
STATEMANAGER.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
STATEMANAGER.SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
STATEMANAGER.DrawIndexedPrimitive(ePrimitiveType, 0, m_iPatchTerrainVertexCount, 0, wPrimitiveCount);
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSU, D3DTADDRESS_CLAMP);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ADDRESSV, D3DTADDRESS_CLAMP);
STATEMANAGER.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
STATEMANAGER.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
STATEMANAGER.SetRenderState(D3DRS_FOGCOLOR, previousFogColor);
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, FALSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
++renderedSplatCount;
++m_iRenderedSplatNum;
}
++m_iRenderedPatchNum;
m_iRenderedSplatNumSqSum += renderedSplatCount * renderedSplatCount;
}
@@ -3,6 +3,8 @@
#include "GameLib/StdAfx.h"
#include "EterLib/StateManager.h"
#include "EterLib/ResourceManager.h"
#include "EterBase/Timer.h"
#include "../EterLib/RenderCommands3D.h"
#include "GameLib/MapOutdoor.h"
#include "GameLib/TerrainPatch.h"
@@ -24,7 +26,133 @@ void CMapOutdoor::UnloadWaterTexture()
m_WaterInstances[i].Destroy();
}
// PORT: the water patches are drawn by the Godot side.
void CMapOutdoor::RenderWater()
{
if (!IsNativeTerrainRenderEnabled())
return;
if (m_PatchVector.empty())
return;
if (!IsVisiblePart(PART_WATER))
return;
D3DXMATRIX matTexTransformWater;
STATEMANAGER.SaveRenderState(D3DRS_ZWRITEENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
STATEMANAGER.SaveRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
STATEMANAGER.SaveRenderState(D3DRS_COLORVERTEX, TRUE);
STATEMANAGER.SaveRenderState(D3DRS_LIGHTING, FALSE);
CGraphicImageInstance& rkWaterInst = m_WaterInstances[((ELTimer_GetMSec() / 70) % 30)];
if (!rkWaterInst.IsEmpty() && rkWaterInst.GetTexturePointer())
STATEMANAGER.SetTexture(0, rkWaterInst.GetTexturePointer()->GetD3DTexture());
else
STATEMANAGER.SetTexture(0, NULL);
D3DXMatrixScaling(&matTexTransformWater, m_fWaterTexCoordBase, -m_fWaterTexCoordBase, 0.0f);
D3DXMatrixMultiply(&matTexTransformWater, &m_matViewInverse, &matTexTransformWater);
STATEMANAGER.SaveTransform(D3DTS_TEXTURE0, &matTexTransformWater);
STATEMANAGER.SaveVertexShader(D3DFVF_XYZ | D3DFVF_DIFFUSE);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
STATEMANAGER.SaveTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
STATEMANAGER.SetTexture(1, NULL);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
static float s_fWaterHeightCurrent = 0;
static float s_fWaterHeightBegin = 0;
static float s_fWaterHeightEnd = 0;
static DWORD s_dwLastHeightChangeTime = CTimer::Instance().GetCurrentMillisecond();
static DWORD s_dwBlendtime = 300;
if ((CTimer::Instance().GetCurrentMillisecond() - s_dwLastHeightChangeTime) > s_dwBlendtime)
{
s_dwBlendtime = random_range(1000, 3000);
if (s_fWaterHeightEnd == 0)
s_fWaterHeightEnd = -static_cast<float>(random_range(0, 15));
else
s_fWaterHeightEnd = 0;
s_fWaterHeightBegin = s_fWaterHeightCurrent;
s_dwLastHeightChangeTime = CTimer::Instance().GetCurrentMillisecond();
}
s_fWaterHeightCurrent = s_fWaterHeightBegin + (s_fWaterHeightEnd - s_fWaterHeightBegin) * (float)((CTimer::Instance().GetCurrentMillisecond() - s_dwLastHeightChangeTime) / (float)s_dwBlendtime);
m_matWorldForCommonUse._43 = s_fWaterHeightCurrent;
m_matWorldForCommonUse._41 = 0.0f;
m_matWorldForCommonUse._42 = 0.0f;
STATEMANAGER.SetTransform(D3DTS_WORLD, &m_matWorldForCommonUse);
for (auto i = m_PatchVector.begin(); i != m_PatchVector.end(); ++i)
{
DrawWater(i->second);
}
m_matWorldForCommonUse._43 = 0.0f;
STATEMANAGER.RestoreVertexShader();
STATEMANAGER.RestoreTransform(D3DTS_TEXTURE0);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MINFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MAGFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_MIPFILTER);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSU);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_ADDRESSV);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXCOORDINDEX);
STATEMANAGER.RestoreTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS);
STATEMANAGER.RestoreRenderState(D3DRS_LIGHTING);
STATEMANAGER.RestoreRenderState(D3DRS_DIFFUSEMATERIALSOURCE);
STATEMANAGER.RestoreRenderState(D3DRS_COLORVERTEX);
STATEMANAGER.RestoreRenderState(D3DRS_ZWRITEENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHABLENDENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
}
void CMapOutdoor::DrawWater(long patchnum)
{
assert(NULL != m_pTerrainPatchProxyList);
if (!m_pTerrainPatchProxyList)
return;
CTerrainPatchProxy& rkTerrainPatchProxy = m_pTerrainPatchProxyList[patchnum];
if (!rkTerrainPatchProxy.isUsed())
return;
if (!rkTerrainPatchProxy.isWaterExists())
return;
CGraphicVertexBuffer* pkVB = rkTerrainPatchProxy.GetWaterVertexBufferPointer();
if (!pkVB)
return;
if (!pkVB->GetD3DVertexBuffer())
return;
UINT uPriCount = rkTerrainPatchProxy.GetWaterFaceCount();
if (!uPriCount)
return;
STATEMANAGER.SetStreamSource(0, pkVB->GetD3DVertexBuffer(), sizeof(SWaterVertex));
STATEMANAGER.DrawPrimitive(D3DPT_TRIANGLELIST, 0, uPriCount);
ms_faceCount += uPriCount;
}
+190 -5
View File
@@ -8,6 +8,7 @@
#include "EterBase/Timer.h"
#include "EterLib/Util.h"
#include "EterLib/GrpBase.h"
#include "EterLib/Camera.h"
#include "../EterLib/HostCursor.h"
#include "UserInterface/PythonNetworkStream.h"
#include "UserInterface/AccountConnector.h"
@@ -86,7 +87,11 @@ std::unique_ptr<CEffectManager> g_effect_manager;
class GameApplicationAdapter final : public IAbstractApplication
{
public:
void GetMousePosition(POINT* point) override { if (point) *point = {}; }
void GetMousePosition(POINT* point) override
{
if (point)
CPythonApplication::Instance().GetMousePosition(point);
}
float GetGlobalTime() override { return CTimer::Instance().GetCurrentSecond(); }
float GetGlobalElapsedTime() override { return CTimer::Instance().GetElapsedSecond(); }
void SkipRenderBuffering(DWORD) override {}
@@ -144,6 +149,7 @@ struct GameSingletons
CPythonSystem pySystem; // last CPythonApplication member (m_pySystem)
};
std::unique_ptr<GameSingletons> g_game_singletons;
bool g_host_hardware_cursor_enabled = false;
bool fail(std::string* error, const std::string& text)
{
@@ -229,6 +235,26 @@ void init_2v0_gameplay_stubs();
namespace PythonBoot
{
void SetHostHardwareCursorEnabled(bool enabled)
{
g_host_hardware_cursor_enabled = enabled;
}
bool HostHardwareCursorEnabled()
{
return g_host_hardware_cursor_enabled;
}
int CursorShape()
{
return g_game_singletons ? CPythonApplication::Instance().GetCursorNum() : 0;
}
bool CursorVisible()
{
return !g_game_singletons || CPythonApplication::Instance().GetCursorVisible();
}
std::string CurrentMapName()
{
if (!g_game_singletons)
@@ -255,9 +281,43 @@ void SetUISize(int width, int height)
}
}
void update_3d_pick_ray()
{
if (!g_game_singletons || !g_window_manager)
return;
if (!CPythonBackground::Instance().IsMapReady())
return;
long lx = 0, ly = 0;
g_window_manager->GetMousePosition(lx, ly);
const long sw = g_window_manager->GetScreenWidth();
const long sh = g_window_manager->GetScreenHeight();
if (sw <= 0 || sh <= 0)
return;
CScreen s;
s.SetPerspective(30.0f, g_window_manager->GetAspect(), 100.0f, CPythonBackground::Instance().GetFarClip());
s.UpdateViewMatrix();
s.BuildViewFrustum();
s.SetCursorPosition(lx, ly, sw, sh);
POINT ptMouse{static_cast<LONG>(lx), static_cast<LONG>(ly)};
CPythonItem::Instance().Update(ptMouse);
CPythonCharacterManager::Instance().Pick();
}
void UIMouseMove(int x, int y)
{
MtHostSetCursor(x, y);
if (g_game_singletons)
{
CPythonApplication::Instance().OnMouseMove(x, y);
if (g_window_manager)
{
long lx = x, ly = y;
g_window_manager->GetMousePosition(lx, ly);
MtHostSetCursor(static_cast<int>(lx), static_cast<int>(ly));
}
update_3d_pick_ray();
return;
}
if (g_window_manager) g_window_manager->RunMouseMove(x, y);
}
@@ -265,16 +325,55 @@ void UIMouseButton(int button, bool pressed, int x, int y)
{
MtHostSetCursor(x, y);
if (!g_window_manager) return;
g_window_manager->RunMouseMove(x, y);
if (g_game_singletons)
CPythonApplication::Instance().OnMouseMove(x, y);
else
g_window_manager->RunMouseMove(x, y);
update_3d_pick_ray();
switch (button)
{
case 1: if (pressed) g_window_manager->RunMouseLeftButtonDown(x, y); else g_window_manager->RunMouseLeftButtonUp(x, y); break;
case 2: if (pressed) g_window_manager->RunMouseRightButtonDown(x, y); else g_window_manager->RunMouseRightButtonUp(x, y); break;
case 3: if (pressed) g_window_manager->RunMouseMiddleButtonDown(x, y); else g_window_manager->RunMouseMiddleButtonUp(x, y); break;
case 1:
if (pressed)
g_window_manager->RunMouseLeftButtonDown(x, y);
else
g_window_manager->RunMouseLeftButtonUp(x, y);
break;
case 2:
if (pressed)
{
g_window_manager->RunMouseRightButtonDown(x, y);
}
else
{
g_window_manager->RunMouseRightButtonUp(x, y);
if (g_game_singletons)
{
CCamera* pkCmrCur = CCameraManager::Instance().GetCurrentCamera();
if (pkCmrCur && pkCmrCur->IsDraging())
CPythonApplication::Instance().OnMouseMiddleButtonUp(x, y);
}
}
break;
case 3:
if (g_game_singletons)
{
if (pressed) CPythonApplication::Instance().OnMouseMiddleButtonDown(x, y);
else CPythonApplication::Instance().OnMouseMiddleButtonUp(x, y);
}
if (pressed) g_window_manager->RunMouseMiddleButtonDown(x, y); else g_window_manager->RunMouseMiddleButtonUp(x, y);
break;
default: break;
}
}
void UIMouseWheel(int nLen)
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
pkCmrCur->Wheel(nLen);
}
// 40250 CPythonApplication::OnKeyDown/OnKeyUp (PythonApplicationEvent.cpp:130-146): ESC first goes
// to RunPressEscapeKey (the OnPressEscapeKey chain every dialog closes on), then to RunKeyDown.
void UIKey(int key, bool pressed)
@@ -282,12 +381,80 @@ void UIKey(int key, bool pressed)
if (!g_window_manager) return;
if (pressed)
{
CPythonApplication::Instance().KeyDown(key);
if (DIK_ESCAPE == key)
g_window_manager->RunPressEscapeKey();
g_window_manager->RunKeyDown(key);
}
else
{
CPythonApplication::Instance().KeyUp(key);
g_window_manager->RunKeyUp(key);
}
}
void SetMoveDirection(float angleDeg, bool moving)
{
if (!g_game_singletons) return;
if (moving)
CPythonPlayer::Instance().NEW_MoveToDirection(angleDeg);
else
CPythonPlayer::Instance().NEW_Stop();
}
void SetAttackKey(bool pressed)
{
if (!g_game_singletons) return;
CPythonPlayer::Instance().SetAttackKeyState(pressed);
}
void CameraBeginDrag(int x, int y)
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
pkCmrCur->BeginDrag(x, y);
}
void CameraDrag(int x, int y)
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
{
POINT pt;
pkCmrCur->Drag(x, y, &pt);
}
}
void CameraEndDrag()
{
CCameraManager& rkCmrMgr = CCameraManager::Instance();
CCamera* pkCmrCur = rkCmrMgr.GetCurrentCamera();
if (pkCmrCur)
pkCmrCur->EndDrag();
}
bool IsPointInsideActiveUI(int x, int y)
{
if (!g_window_manager) return false;
return g_window_manager->IsPointInsideActiveUI(x, y);
}
PlayerStatusInfo GetPlayerStatusInfo()
{
PlayerStatusInfo info{};
if (!g_game_singletons) return info;
info.level = CPythonPlayer::Instance().GetStatus(POINT_LEVEL);
info.hp = CPythonPlayer::Instance().GetStatus(POINT_HP);
info.max_hp = CPythonPlayer::Instance().GetStatus(POINT_MAX_HP);
info.sp = CPythonPlayer::Instance().GetStatus(POINT_SP);
info.max_sp = CPythonPlayer::Instance().GetStatus(POINT_MAX_SP);
info.exp = CPythonPlayer::Instance().GetStatus(POINT_EXP);
info.max_exp = CPythonPlayer::Instance().GetStatus(POINT_NEXT_EXP);
const char* szName = CPythonPlayer::Instance().GetName();
if (szName) info.name = szName;
return info;
}
// 40250 CPythonApplication::WindowProcedure (PythonApplicationProcedure.cpp:111-117): WM_CHAR goes to
@@ -346,6 +513,14 @@ void UIRender()
if (g_window_manager) g_window_manager->Render();
}
bool IsSoftwareCursorVisible()
{
if (!g_game_singletons)
return false;
return CPythonApplication::Instance().GetCursorMode() == CPythonApplication::CURSOR_MODE_SOFTWARE &&
CPythonApplication::Instance().GetCursorVisible();
}
bool Start(const char* stdlib_path, std::string* error)
{
if (g_launcher)
@@ -647,6 +822,16 @@ void Stop()
g_fiber.finished = false;
// 40250 Main(): pyLauncher.Clear() runs before app->Destroy()/delete app, so the window manager
// (a CPythonApplication member) is still alive while Py_Finalize runs the windows' __del__.
if (Py_IsInitialized())
{
PyGILState_STATE gil = PyGILState_Ensure();
PyRun_SimpleString(
"import sys\n"
"_mm = sys.modules.get('mouseModule')\n"
"if _mm and hasattr(_mm, 'mouseController'):\n"
" getattr(_mm.mouseController, 'cursorDict', {}).clear()\n");
PyGILState_Release(gil);
}
g_launcher->Clear();
g_game_singletons.reset();
g_game_application.reset();
@@ -24,6 +24,11 @@ namespace PythonBoot
// before the launcher exists.
bool Start(const char* stdlib_path, std::string* error);
bool IsRunning();
// The SDL native client supplies an OS cursor; the Godot host keeps its software cursor.
void SetHostHardwareCursorEnabled(bool enabled);
bool HostHardwareCursorEnabled();
int CursorShape();
bool CursorVisible();
// 40250: RunMainScript(CPythonLauncher&, const char*) — the module initializers, __DEBUG__,
// __COMMAND_LINE__ and RunFile("system.py"). 2V0-e registers temporary observable gameplay modules;
@@ -56,13 +61,34 @@ bool Evaluate(const char* expression, std::string* result, std::string* error);
void SetUISize(int width, int height);
void UIMouseMove(int x, int y);
void UIMouseButton(int button, bool pressed, int x, int y);
void UIMouseWheel(int nLen);
void UIKey(int key, bool pressed);
// Mobile touch & joystick controls
void SetMoveDirection(float angleDeg, bool moving);
void SetAttackKey(bool pressed);
void CameraBeginDrag(int x, int y);
void CameraDrag(int x, int y);
void CameraEndDrag();
bool IsPointInsideActiveUI(int x, int y);
struct PlayerStatusInfo {
int level = 1;
int hp = 0;
int max_hp = 0;
int sp = 0;
int max_sp = 0;
int exp = 0;
int max_exp = 0;
std::string name;
};
PlayerStatusInfo GetPlayerStatusInfo();
// WM_CHAR (a Unicode code point) and WM_KEYDOWN (a Win32 VK code) for CPythonIME and OnIMEKeyDown.
void UIChar(unsigned codepoint);
void UIIMEKeyDown(int vkey);
// While IsAppLooping(), UIUpdate is AppFrame() and UIRender keeps the commands that Process() drew.
void UIUpdate();
void UIRender();
bool IsSoftwareCursorVisible();
// 40250 Main() calls Clear() and lets the launcher leave scope.
void Stop();
@@ -1,84 +1,241 @@
// Platform skeleton for SpeedTreeLib/SpeedTreeForest.h (40250 SpeedTreeLib/SpeedTreeForest.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
#include "SpeedTreeLib/StdAfx.h"
#include "SpeedTreeLib/SpeedTreeForest.h"
#include "../PlatformStub.h"
#include "EterPack/EterPackManager.h"
#include "GameLib/Property.h"
#include "GameLib/PropertyManager.h"
#include "../EterLib/RenderCommands3D.h"
#include <algorithm>
#include <cfloat>
#include <cmath>
#include <cstdlib>
#include <cstring>
CSpeedTreeForest::CSpeedTreeForest()
: m_fWindStrength(0.2f)
, m_fAccumTime(0.0f)
{
MT_PLATFORM_STUB();
std::memset(m_afLighting, 0, sizeof(m_afLighting));
std::memset(m_afFog, 0, sizeof(m_afFog));
m_afForestExtents[0] = m_afForestExtents[1] = m_afForestExtents[2] = FLT_MAX;
m_afForestExtents[3] = m_afForestExtents[4] = m_afForestExtents[5] = -FLT_MAX;
}
CSpeedTreeForest::~CSpeedTreeForest()
{
MT_PLATFORM_STUB();
}
auto CSpeedTreeForest::ClearMainTree() -> void
{
MT_PLATFORM_STUB();
Clear();
}
auto CSpeedTreeForest::GetMainTree(DWORD, CSpeedTreeWrapper **, const char *) -> BOOL
auto CSpeedTreeForest::GetMainTree(DWORD dwCRC, CSpeedTreeWrapper ** ppMainTree, const char * c_pszFileName) -> BOOL
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<BOOL>();
if (!ppMainTree)
return FALSE;
*ppMainTree = NULL;
if (!IsNativeTerrainRenderEnabled())
return FALSE;
TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC);
CSpeedTreeWrapper * pTree = NULL;
if (itor != m_pMainTreeMap.end())
{
pTree = itor->second;
}
else
{
if (!c_pszFileName || !c_pszFileName[0])
return FALSE;
CMappedFile file;
LPCVOID c_pvData = NULL;
if (!CEterPackManager::Instance().Get(file, c_pszFileName, &c_pvData))
return FALSE;
float fSize = 1000.0f;
float fVariance = 0.0f;
CProperty * pProperty = NULL;
if (CPropertyManager::InstancePtr() && CPropertyManager::Instance().Get(dwCRC, &pProperty) && pProperty)
{
const char * c_pszTreeSize = NULL;
const char * c_pszTreeVariance = NULL;
if (pProperty->GetString("TreeSize", &c_pszTreeSize) && c_pszTreeSize)
fSize = static_cast<float>( std::atof(c_pszTreeSize));
if (pProperty->GetString("TreeVariance", &c_pszTreeVariance) && c_pszTreeVariance)
fVariance = static_cast<float>(std::atof(c_pszTreeVariance));
}
pTree = new CSpeedTreeWrapper;
if (!pTree->LoadTree(c_pszFileName, static_cast<const BYTE *>(c_pvData), file.Size(), 1, fSize, fVariance))
{
delete pTree;
return FALSE;
}
m_pMainTreeMap.insert(TTreeMap::value_type(dwCRC, pTree));
file.Destroy();
}
*ppMainTree = pTree;
return TRUE;
}
auto CSpeedTreeForest::GetMainTree(DWORD) -> CSpeedTreeWrapper *
auto CSpeedTreeForest::GetMainTree(DWORD dwCRC) -> CSpeedTreeWrapper *
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<CSpeedTreeWrapper *>();
TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC);
if (itor == m_pMainTreeMap.end())
return NULL;
return itor->second;
}
auto CSpeedTreeForest::DeleteMainTree(DWORD) -> void
auto CSpeedTreeForest::DeleteMainTree(DWORD dwCRC) -> void
{
MT_PLATFORM_STUB();
TTreeMap::iterator itor = m_pMainTreeMap.find(dwCRC);
if (itor == m_pMainTreeMap.end())
return;
CSpeedTreeWrapper * pMainTree = itor->second;
UINT uiCount = 0;
CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount);
for (UINT i = 0; i < uiCount; ++i)
delete ppInstances[i];
delete pMainTree;
m_pMainTreeMap.erase(itor);
}
auto CSpeedTreeForest::CreateInstance(float, float, float, DWORD, const char *) -> CSpeedTreeWrapper *
auto CSpeedTreeForest::CreateInstance(float x, float y, float z, DWORD dwTreeCRC, const char * c_pszTreeName) -> CSpeedTreeWrapper *
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<CSpeedTreeWrapper *>();
if (!IsNativeTerrainRenderEnabled())
return NULL;
CSpeedTreeWrapper * pMainTree = NULL;
if (!GetMainTree(dwTreeCRC, &pMainTree, c_pszTreeName) || !pMainTree)
return NULL;
CSpeedTreeWrapper * pTreeInst = pMainTree->MakeInstance();
if (!pTreeInst)
return NULL;
pTreeInst->SetPosition(x, y, z);
pTreeInst->RegisterBoundingSphere();
AdjustExtents(x, y, z);
return pTreeInst;
}
auto CSpeedTreeForest::DeleteInstance(CSpeedTreeWrapper *) -> void
auto CSpeedTreeForest::DeleteInstance(CSpeedTreeWrapper * pInstance) -> void
{
MT_PLATFORM_STUB();
if (!pInstance)
return;
CSpeedTreeWrapper * pParentTree = pInstance->InstanceOf();
if (!pParentTree)
return;
pParentTree->DeleteInstance(pInstance);
}
auto CSpeedTreeForest::UpdateSystem(float) -> void
auto CSpeedTreeForest::UpdateSystem(float fCurrentTime) -> void
{
MT_PLATFORM_STUB();
static float fLastTime = fCurrentTime;
float fElapsedTime = fCurrentTime - fLastTime;
fLastTime = fCurrentTime;
if (fElapsedTime > 0.0f && fElapsedTime < 10.0f)
m_fAccumTime += fElapsedTime;
SetupWindMatrices(m_fAccumTime);
}
auto CSpeedTreeForest::Clear() -> void
{
MT_PLATFORM_STUB();
TTreeMap::iterator itor = m_pMainTreeMap.begin();
UINT uiCount = 0;
while (itor != m_pMainTreeMap.end())
{
CSpeedTreeWrapper * pMainTree = (itor++)->second;
if (!pMainTree)
continue;
CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount);
for (UINT i = 0; i < uiCount; ++i)
delete ppInstances[i];
delete pMainTree;
}
m_pMainTreeMap.clear();
}
auto CSpeedTreeForest::SetLight(const float *, const float *, const float *) -> void
auto CSpeedTreeForest::SetLight(const float * afDirection, const float * afAmbient, const float * afDiffuse) -> void
{
MT_PLATFORM_STUB();
if (!afDirection || !afAmbient || !afDiffuse)
return;
m_afLighting[0] = afDirection[0];
m_afLighting[1] = afDirection[1];
m_afLighting[2] = afDirection[2];
m_afLighting[3] = 1.0f;
m_afLighting[4] = afAmbient[0];
m_afLighting[5] = afAmbient[1];
m_afLighting[6] = afAmbient[2];
m_afLighting[7] = afAmbient[3];
m_afLighting[8] = afDiffuse[0];
m_afLighting[9] = afDiffuse[1];
m_afLighting[10] = afDiffuse[2];
m_afLighting[11] = afDiffuse[3];
}
auto CSpeedTreeForest::SetFog(float, float) -> void
auto CSpeedTreeForest::SetFog(float fFogNear, float fFogFar) -> void
{
MT_PLATFORM_STUB();
const float denom = (fFogFar - fFogNear);
const float c_fFogLinearScale = (std::fabs(denom) > 1e-4f) ? (1.0f / denom) : 0.0f;
m_afFog[0] = fFogNear;
m_afFog[1] = fFogFar;
m_afFog[2] = c_fFogLinearScale;
m_afFog[3] = 0.0f;
}
auto CSpeedTreeForest::SetWindStrength(float) -> void
auto CSpeedTreeForest::SetWindStrength(float fStrength) -> void
{
MT_PLATFORM_STUB();
if (m_fWindStrength == fStrength)
return;
m_fWindStrength = fStrength;
TTreeMap::iterator itor = m_pMainTreeMap.begin();
UINT uiCount = 0;
while (itor != m_pMainTreeMap.end())
{
CSpeedTreeWrapper * pMainTree = (itor++)->second;
if (!pMainTree)
continue;
CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount);
for (UINT i = 0; i < uiCount; ++i)
{
if (ppInstances[i] && ppInstances[i]->GetSpeedTree())
ppInstances[i]->GetSpeedTree()->SetWindStrength(m_fWindStrength);
}
}
}
auto CSpeedTreeForest::SetupWindMatrices(float) -> void
auto CSpeedTreeForest::SetupWindMatrices(float /*fTimeInSecs*/) -> void
{
MT_PLATFORM_STUB();
}
auto CSpeedTreeForest::AdjustExtents(float, float, float) -> void
auto CSpeedTreeForest::AdjustExtents(float x, float y, float z) -> void
{
MT_PLATFORM_STUB();
m_afForestExtents[0] = std::min(m_afForestExtents[0], x);
m_afForestExtents[1] = std::min(m_afForestExtents[1], y);
m_afForestExtents[2] = std::min(m_afForestExtents[2], z);
m_afForestExtents[3] = std::max(m_afForestExtents[3], x);
m_afForestExtents[4] = std::max(m_afForestExtents[4], y);
m_afForestExtents[5] = std::max(m_afForestExtents[5], z);
}
@@ -1,43 +1,163 @@
// Platform skeleton for SpeedTreeLib/SpeedTreeForestDirectX8.h (40250 SpeedTreeLib/SpeedTreeForestDirectX8.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
#include "SpeedTreeLib/StdAfx.h"
#include "SpeedTreeLib/SpeedTreeForestDirectX8.h"
#include "../PlatformStub.h"
#include "EterBase/Timer.h"
#include "EterLib/Camera.h"
#include "EterLib/StateManager.h"
#include "../EterLib/RenderCommands3D.h"
CSpeedTreeForestDirectX8::CSpeedTreeForestDirectX8()
: m_pDx(NULL)
, m_dwBranchVertexShader(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1)
, m_dwLeafVertexShader(D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1)
{
MT_PLATFORM_STUB();
}
CSpeedTreeForestDirectX8::~CSpeedTreeForestDirectX8()
{
MT_PLATFORM_STUB();
Clear();
}
auto CSpeedTreeForestDirectX8::UploadWindMatrix(unsigned int, const float *) const -> void
auto CSpeedTreeForestDirectX8::UploadWindMatrix(unsigned int uiLocation, const float * pMatrix) const -> void
{
MT_PLATFORM_STUB();
if (pMatrix)
STATEMANAGER.SetVertexShaderConstant(uiLocation, pMatrix, 4);
}
auto CSpeedTreeForestDirectX8::UpdateCompundMatrix(const D3DXVECTOR3 &, const D3DXMATRIX &, const D3DXMATRIX &) -> void
auto CSpeedTreeForestDirectX8::UpdateCompundMatrix(const D3DXVECTOR3 & /*c_rEyeVec*/, const D3DXMATRIX & c_rmatView, const D3DXMATRIX & c_rmatProj) -> void
{
MT_PLATFORM_STUB();
D3DXMATRIX matBlendShader;
D3DXMatrixMultiply(&matBlendShader, &c_rmatView, &c_rmatProj);
D3DXMatrixTranspose(&matBlendShader, &matBlendShader);
STATEMANAGER.SetVertexShaderConstant(0, &matBlendShader, 4);
}
auto CSpeedTreeForestDirectX8::Render(unsigned long) -> void
auto CSpeedTreeForestDirectX8::Render(unsigned long ulRenderBitVector) -> void
{
MT_PLATFORM_STUB();
if (!IsNativeTerrainRenderEnabled())
return;
UpdateSystem(CTimer::Instance().GetCurrentSecond());
if (m_pMainTreeMap.empty())
return;
if (!(ulRenderBitVector & Forest_RenderToShadow) && !(ulRenderBitVector & Forest_RenderToMiniMap))
{
CCamera * pCamera = CCameraManager::Instance().GetCurrentCamera();
if (pCamera)
UpdateCompundMatrix(pCamera->GetEye(), ms_matView, ms_matProj);
}
DWORD dwLightState = STATEMANAGER.GetRenderState(D3DRS_LIGHTING);
DWORD dwColorVertexState = STATEMANAGER.GetRenderState(D3DRS_COLORVERTEX);
DWORD dwFogVertexMode = STATEMANAGER.GetRenderState(D3DRS_FOGVERTEXMODE);
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, TRUE);
STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, TRUE);
TTreeMap::iterator itor = m_pMainTreeMap.begin();
UINT uiCount = 0;
while (itor != m_pMainTreeMap.end())
{
CSpeedTreeWrapper * pMainTree = (itor++)->second;
if (!pMainTree)
continue;
CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount);
for (UINT i = 0; i < uiCount; ++i)
{
if (ppInstances[i])
ppInstances[i]->Advance();
}
}
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
STATEMANAGER.SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
STATEMANAGER.SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
STATEMANAGER.SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHATESTENABLE, FALSE);
STATEMANAGER.SaveRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
STATEMANAGER.SaveRenderState(D3DRS_ALPHAREF, 0x00000060);
STATEMANAGER.SaveRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
STATEMANAGER.SetVertexShader(m_dwBranchVertexShader);
// Render branches
if (ulRenderBitVector & Forest_RenderBranches)
{
STATEMANAGER.SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
itor = m_pMainTreeMap.begin();
while (itor != m_pMainTreeMap.end())
{
CSpeedTreeWrapper * pMainTree = (itor++)->second;
if (!pMainTree)
continue;
CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount);
pMainTree->SetupBranchForTreeType();
for (UINT i = 0; i < uiCount; ++i)
{
if (ppInstances[i] && ppInstances[i]->isShow())
ppInstances[i]->RenderBranches();
}
}
}
// Render leaves
if (ulRenderBitVector & Forest_RenderLeaves)
{
STATEMANAGER.SetVertexShader(m_dwLeafVertexShader);
STATEMANAGER.SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);
STATEMANAGER.SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATER);
STATEMANAGER.SetRenderState(D3DRS_ALPHAREF, 0x00000060);
STATEMANAGER.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
itor = m_pMainTreeMap.begin();
while (itor != m_pMainTreeMap.end())
{
CSpeedTreeWrapper * pMainTree = (itor++)->second;
if (!pMainTree)
continue;
CSpeedTreeWrapper ** ppInstances = pMainTree->GetInstances(uiCount);
pMainTree->SetupLeafForTreeType();
for (UINT i = 0; i < uiCount; ++i)
{
if (ppInstances[i] && ppInstances[i]->isShow())
ppInstances[i]->RenderLeaves();
}
pMainTree->EndLeafForTreeType();
}
}
STATEMANAGER.SetRenderState(D3DRS_LIGHTING, dwLightState);
STATEMANAGER.SetRenderState(D3DRS_COLORVERTEX, dwColorVertexState);
STATEMANAGER.SetRenderState(D3DRS_FOGVERTEXMODE, dwFogVertexMode);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHATESTENABLE);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHAFUNC);
STATEMANAGER.RestoreRenderState(D3DRS_ALPHAREF);
STATEMANAGER.RestoreRenderState(D3DRS_CULLMODE);
}
auto CSpeedTreeForestDirectX8::SetRenderingDevice(LPDIRECT3DDEVICE8) -> bool
auto CSpeedTreeForestDirectX8::SetRenderingDevice(LPDIRECT3DDEVICE8 pDevice) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
m_pDx = pDevice;
return InitVertexShaders();
}
auto CSpeedTreeForestDirectX8::InitVertexShaders() -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
m_dwBranchVertexShader = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1;
m_dwLeafVertexShader = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX1;
CSpeedTreeWrapper::SetVertexShaders(m_dwBranchVertexShader, m_dwLeafVertexShader);
return true;
}
File diff suppressed because it is too large Load Diff
@@ -1,22 +0,0 @@
// Platform skeleton for SphereLib/frustum.h (40250 SphereLib/frustum.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
#include "SphereLib/StdAfx.h"
#include "SphereLib/frustum.h"
#include "../PlatformStub.h"
auto Frustum::BuildViewFrustum(D3DXMATRIX &) -> void
{
MT_PLATFORM_STUB();
}
auto Frustum::BuildViewFrustum2(D3DXMATRIX &, float, float, float, float, const D3DXVECTOR3 &, const D3DXVECTOR3 &) -> void
{
MT_PLATFORM_STUB();
}
auto Frustum::ViewVolumeTest(const Vector3d &, const float) const -> ViewState
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<ViewState>();
}
@@ -1,49 +0,0 @@
// Platform skeleton for SphereLib/sphere.h (40250 SphereLib/sphere.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
#include "SphereLib/StdAfx.h"
#include "SphereLib/sphere.h"
#include "../PlatformStub.h"
SphereInterface::SphereInterface()
{
MT_PLATFORM_STUB();
}
SphereInterface::~SphereInterface()
{
MT_PLATFORM_STUB();
}
auto Sphere::Set(const Vector3d &, float) -> void
{
MT_PLATFORM_STUB();
}
auto Sphere::Compute(const SphereInterface &) -> void
{
MT_PLATFORM_STUB();
}
auto Sphere::RayIntersection(const Vector3d &, const Vector3d &, float, Vector3d *) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto Sphere::RayIntersection(const Vector3d &, const Vector3d &, Vector3d *) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto Sphere::RayIntersectionInFront(const Vector3d &, const Vector3d &, Vector3d *) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto Sphere::Report() -> void
{
MT_PLATFORM_STUB();
}
@@ -1,138 +0,0 @@
// Platform skeleton for SphereLib/spherepack.h (40250 SphereLib/spherepack.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
#include "SphereLib/StdAfx.h"
#include "SphereLib/spherepack.h"
#include "../PlatformStub.h"
auto SpherePack::LostChild(SpherePack *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePack::Render(unsigned int) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePack::Recompute(float) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto SpherePack::VisibilityTest(const Frustum &, SpherePackCallback *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePack::RayTrace(const Vector3d &, const Vector3d &, float, SpherePackCallback *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePack::RangeTest(const Vector3d &, float, SpherePackCallback *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePack::PointTest2d(const Vector3d &, SpherePackCallback *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePack::Reset() -> void
{
MT_PLATFORM_STUB();
}
SpherePackFactory::SpherePackFactory(int, float, float, float)
{
MT_PLATFORM_STUB();
}
SpherePackFactory::~SpherePackFactory()
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::Process() -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::AddSphere_(const Vector3d &, float, void *, bool, int) -> SpherePack *
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<SpherePack *>();
}
auto SpherePackFactory::AddIntegrate(SpherePack *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::AddRecompute(SpherePack *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::Integrate(SpherePack *, SpherePack *, float) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::Render() -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::Remove(SpherePack *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::FrustumTest(const Frustum &, SpherePackCallback *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::RayTrace(const Vector3d &, const Vector3d &, SpherePackCallback *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::RangeTest(const Vector3d &, float, SpherePackCallback *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::PointTest2d(const Vector3d &, SpherePackCallback *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::RayTraceCallback(const Vector3d &, const Vector3d &, float, const Vector3d &, SpherePack *) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::RangeTestCallback(const Vector3d &, float, SpherePack *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::PointTest2dCallback(const Vector3d &, SpherePack *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::VisibilityCallback(const Frustum &, SpherePack *, ViewState) -> void
{
MT_PLATFORM_STUB();
}
auto SpherePackFactory::Reset() -> void
{
MT_PLATFORM_STUB();
}
@@ -1,12 +0,0 @@
// Platform skeleton for SphereLib/vector.h, generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
#include "SphereLib/StdAfx.h"
#include "SphereLib/vector.h"
#include "../PlatformStub.h"
auto Vector3d::IsInStaticRange() const -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
@@ -25,8 +25,9 @@
//
// PORT: there is no CPythonApplication object yet (its members are the whole game: background,
// network stream, player, ...), so these functions only use the singletons PythonBoot owns
// (UI::CWindowManager, CTimer, CResourceManager) and never touch a data member.
CPythonApplication* CPythonApplication::ms_pInstance = nullptr;
alignas(alignof(CPythonApplication)) static char s_app_storage[sizeof(CPythonApplication)] = {};
CPythonApplication* CPythonApplication::ms_pInstance = reinterpret_cast<CPythonApplication*>(s_app_storage);
extern double g_specularSpd;
void CPythonApplication::ShowWebPage(const char*, const RECT&) { MT_PLATFORM_STUB(); }
void CPythonApplication::MoveWebPage(const RECT&) { MT_PLATFORM_STUB(); }
@@ -134,6 +135,16 @@ bool CPythonApplication::Create(PyObject*, const char*, int, int, int)
// 40250 PythonApplication.cpp:1260.
CPythonTextTail::Instance().Initialize();
if (IsNativeTerrainRenderEnabled())
{
const bool use_hardware_cursor = PythonBoot::HostHardwareCursorEnabled();
if (CPythonSystem::InstancePtr() && CPythonSystem::Instance().GetConfig())
CPythonSystem::Instance().GetConfig()->is_software_cursor = !use_hardware_cursor;
SetCursorMode(use_hardware_cursor ? CURSOR_MODE_HARDWARE : CURSOR_MODE_SOFTWARE);
CGrannyMaterial::CreateSphereMap(0, "d:/ymir work/special/spheremap.jpg");
CGrannyMaterial::CreateSphereMap(1, "d:/ymir work/special/spheremap01.jpg");
}
return true;
}
// 40250 PythonApplication.cpp:105
@@ -216,6 +227,7 @@ void CPythonApplication::UpdateGame()
s.SetPerspective(30.0f,fAspect, 100.0f, fFarClip);
s.BuildViewFrustum();
s.SetCursorPosition(ptMouse.x, ptMouse.y, UI::CWindowManager::Instance().GetScreenWidth(), UI::CWindowManager::Instance().GetScreenHeight());
}
TPixelPosition kPPosMainActor;
@@ -262,8 +274,13 @@ bool CPythonApplication::Process()
CResourceManager::Instance().Update();
OnCameraUpdate();
if (IsNativeTerrainRenderEnabled())
OnMouseUpdate();
OnUIUpdate();
if (IsNativeTerrainRenderEnabled())
CGrannyMaterial::TranslateSpecularMatrix(g_specularSpd, g_specularSpd, 0.0f);
// PORT: the render block without the lost-device restore, ClearDepthBuffer, Show and the render-time
// statistics (Godot clears and presents the frame). The frame's UI and 3D command lists restart here.
CCullingManager::Instance().Update();
@@ -275,6 +292,14 @@ bool CPythonApplication::Process()
rkGraphic.SetInterfaceRenderState();
OnUIRender();
if (IsNativeTerrainRenderEnabled())
{
rkGraphic.SetInterfaceRenderState();
unsigned cw = 0, ch = 0;
UIRenderGetSize(&cw, &ch);
UIRenderSetClip(0.0f, 0.0f, float(cw), float(ch));
OnMouseRender();
}
rkGraphic.End();
}
@@ -11,7 +11,10 @@ void CPythonApplication::OnCameraUpdate()
{
CCamera* pkCameraMgr = CCameraManager::Instance().GetCurrentCamera();
if (pkCameraMgr)
{
pkCameraMgr->Update();
CPythonGraphic::Instance().UpdateViewMatrix();
}
}
}
@@ -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;
+369 -70
View File
@@ -12,7 +12,13 @@
#include "platform/EterLib/UIRenderCommands.h"
#include "platform/EterLib/RenderCommands3D.h"
#include "platform/MilesLib/AudioCommands.h"
#include "platform/PackBackend.h"
#include "../../native_render/draw_capture.h"
#include <cstdlib>
#include <cstring>
#include <string>
#include <type_traits>
#include <unordered_map>
#endif
#include <godot_cpp/classes/image.hpp>
@@ -23,6 +29,8 @@
#include <godot_cpp/variant/packed_int32_array.hpp>
#include <godot_cpp/variant/packed_vector2_array.hpp>
#include <godot_cpp/variant/packed_vector3_array.hpp>
#include <godot_cpp/variant/string_name.hpp>
using namespace godot;
@@ -82,53 +90,188 @@ void Metin2PythonHost::ui_mouse_move(int x, int y) { PythonBoot::UIMouseMove(x,
void Metin2PythonHost::ui_mouse_button(int button, bool pressed, int x, int y) {
PythonBoot::UIMouseButton(button, pressed, x, y);
}
void Metin2PythonHost::ui_mouse_wheel(int delta) { PythonBoot::UIMouseWheel(delta); }
void Metin2PythonHost::ui_key(int key, bool pressed) { PythonBoot::UIKey(key, pressed); }
void Metin2PythonHost::ui_char(int codepoint) { PythonBoot::UIChar(unsigned(codepoint)); }
void Metin2PythonHost::ui_ime_key(int vkey) { PythonBoot::UIIMEKeyDown(vkey); }
void Metin2PythonHost::ui_update() { PythonBoot::UIUpdate(); }
namespace {
Dictionary ui_command_item(const UIRenderCommand &command) {
static const StringName s_kind("kind");
static const StringName s_x1("x1");
static const StringName s_y1("y1");
static const StringName s_x2("x2");
static const StringName s_y2("y2");
static const StringName s_argb("argb");
static const StringName s_end_argb("end_argb");
static const StringName s_clip_x1("clip_x1");
static const StringName s_clip_y1("clip_y1");
static const StringName s_clip_x2("clip_x2");
static const StringName s_clip_y2("clip_y2");
static const StringName s_text("text");
static const StringName s_quad("quad");
static const StringName s_uv("uv");
static const StringName s_blend("blend");
static const StringName s_mask("mask");
static const StringName s_mask_uv("mask_uv");
static const StringName s_behind_3d("behind_3d");
static const StringName s_val_bar("bar");
static const StringName s_val_gradient_bar("gradient_bar");
static const StringName s_val_line("line");
static const StringName s_val_image("image");
static const StringName s_val_text("text");
Dictionary item;
item[s_kind] = command.kind == UIRenderCommand::GradientBar ? s_val_gradient_bar :
(command.kind == UIRenderCommand::Bar ? s_val_bar :
(command.kind == UIRenderCommand::Line ? s_val_line :
(command.kind == UIRenderCommand::Image ? s_val_image : s_val_text)));
item[s_x1] = command.x1;
item[s_y1] = command.y1;
item[s_x2] = command.x2;
item[s_y2] = command.y2;
item[s_argb] = static_cast<int64_t>(command.argb);
if (command.kind == UIRenderCommand::GradientBar)
item[s_end_argb] = static_cast<int64_t>(command.end_argb);
item[s_clip_x1] = command.clip_x1;
item[s_clip_y1] = command.clip_y1;
item[s_clip_x2] = command.clip_x2;
item[s_clip_y2] = command.clip_y2;
if (command.kind == UIRenderCommand::Text || command.kind == UIRenderCommand::Image)
item[s_text] = String::utf8(command.text.c_str());
if (command.quad) {
PackedVector2Array quad;
quad.resize(4);
Vector2 *qptr = quad.ptrw();
for (int i = 0; i < 4; ++i)
qptr[i] = Vector2(command.qx[i], command.qy[i]);
item[s_quad] = quad;
PackedVector2Array uv;
uv.resize(4);
Vector2 *uvptr = uv.ptrw();
uvptr[0] = Vector2(command.su, command.sv);
uvptr[1] = Vector2(command.eu, command.sv);
uvptr[2] = Vector2(command.su, command.ev);
uvptr[3] = Vector2(command.eu, command.ev);
item[s_uv] = uv;
item[s_blend] = command.blend;
if (!command.mask.empty()) {
item[s_mask] = String::utf8(command.mask.c_str());
PackedVector2Array mask_uv;
mask_uv.resize(4);
Vector2 *mptr = mask_uv.ptrw();
for (int i = 0; i < 4; ++i)
mptr[i] = Vector2(command.mu[i], command.mv[i]);
item[s_mask_uv] = mask_uv;
}
}
item[s_behind_3d] = command.behind_3d;
return item;
}
} // namespace
Array Metin2PythonHost::ui_render_commands() {
PythonBoot::UIRender();
static std::uint64_t cached_frame_id = UINT64_MAX;
static Array cached_commands;
const std::uint64_t frame_id = UIRenderFrameId();
if (cached_frame_id == frame_id)
return cached_commands;
const auto &commands = UIRenderCommands();
Array out;
for (const auto &command : UIRenderCommands()) {
Dictionary item;
item["kind"] = command.kind == UIRenderCommand::GradientBar ? "gradient_bar" :
(command.kind == UIRenderCommand::Bar ? "bar" :
(command.kind == UIRenderCommand::Line ? "line" :
(command.kind == UIRenderCommand::Image ? "image" : "text")));
item["x1"] = command.x1;
item["y1"] = command.y1;
item["x2"] = command.x2;
item["y2"] = command.y2;
item["argb"] = static_cast<int64_t>(command.argb);
if (command.kind == UIRenderCommand::GradientBar)
item["end_argb"] = static_cast<int64_t>(command.end_argb);
item["clip_x1"] = command.clip_x1;
item["clip_y1"] = command.clip_y1;
item["clip_x2"] = command.clip_x2;
item["clip_y2"] = command.clip_y2;
if (command.kind == UIRenderCommand::Text || command.kind == UIRenderCommand::Image)
item["text"] = String::utf8(command.text.c_str());
if (command.quad) {
PackedVector2Array quad;
for (int i = 0; i < 4; ++i)
quad.push_back(Vector2(command.qx[i], command.qy[i]));
item["quad"] = quad;
item["uv"] = PackedVector2Array({Vector2(command.su, command.sv), Vector2(command.eu, command.sv),
Vector2(command.su, command.ev), Vector2(command.eu, command.ev)});
item["blend"] = command.blend;
if (!command.mask.empty()) {
item["mask"] = String::utf8(command.mask.c_str());
PackedVector2Array mask_uv;
for (int i = 0; i < 4; ++i)
mask_uv.push_back(Vector2(command.mu[i], command.mv[i]));
item["mask_uv"] = mask_uv;
}
}
out.push_back(item);
}
out.resize(static_cast<int64_t>(commands.size()));
for (size_t idx = 0; idx < commands.size(); ++idx)
out[static_cast<int64_t>(idx)] = ui_command_item(commands[idx]);
cached_frame_id = frame_id;
cached_commands = out;
return out;
}
Array Metin2PythonHost::ui_render_commands_batched() {
PythonBoot::UIRender();
static std::uint64_t cached_frame_id = UINT64_MAX;
static Array cached_commands;
const std::uint64_t frame_id = UIRenderFrameId();
if (cached_frame_id == frame_id)
return cached_commands;
const auto &commands = UIRenderCommands();
const auto batchable = [](const UIRenderCommand &c) {
return c.kind == UIRenderCommand::Image && c.quad && c.text.rfind("mem:", 0) == 0 &&
c.mask.empty() && c.blend == 0 && c.x1 >= c.clip_x1 && c.y1 >= c.clip_y1 &&
c.x2 <= c.clip_x2 && c.y2 <= c.clip_y2;
};
Array out;
for (size_t i = 0; i < commands.size();) {
const UIRenderCommand &first = commands[i];
if (!batchable(first)) {
out.push_back(ui_command_item(first));
++i;
continue;
}
size_t end = i + 1;
while (end < commands.size() && batchable(commands[end]) &&
commands[end].text == first.text && commands[end].behind_3d == first.behind_3d)
++end;
if (end == i + 1) {
out.push_back(ui_command_item(first));
i = end;
continue;
}
const int64_t count = static_cast<int64_t>(end - i);
PackedVector2Array points, uvs;
PackedColorArray colors;
PackedInt32Array indices;
points.resize(count * 4);
uvs.resize(count * 4);
colors.resize(count * 4);
indices.resize(count * 6);
Vector2 *point_data = points.ptrw(), *uv_data = uvs.ptrw();
Color *color_data = colors.ptrw();
int32_t *index_data = indices.ptrw();
for (int64_t j = 0; j < count; ++j) {
const UIRenderCommand &c = commands[i + static_cast<size_t>(j)];
const int64_t v = j * 4, t = j * 6;
point_data[v] = Vector2(c.qx[0], c.qy[0]);
point_data[v + 1] = Vector2(c.qx[1], c.qy[1]);
point_data[v + 2] = Vector2(c.qx[3], c.qy[3]);
point_data[v + 3] = Vector2(c.qx[2], c.qy[2]);
uv_data[v] = Vector2(c.su, c.sv);
uv_data[v + 1] = Vector2(c.eu, c.sv);
uv_data[v + 2] = Vector2(c.eu, c.ev);
uv_data[v + 3] = Vector2(c.su, c.ev);
const Color color(((c.argb >> 16) & 255) / 255.0f, ((c.argb >> 8) & 255) / 255.0f,
(c.argb & 255) / 255.0f, ((c.argb >> 24) & 255) / 255.0f);
for (int k = 0; k < 4; ++k)
color_data[v + k] = color;
const int32_t base = static_cast<int32_t>(v);
index_data[t] = base;
index_data[t + 1] = base + 1;
index_data[t + 2] = base + 2;
index_data[t + 3] = base;
index_data[t + 4] = base + 2;
index_data[t + 5] = base + 3;
}
Dictionary item;
item["kind"] = StringName("glyph_batch");
item["text"] = String::utf8(first.text.c_str());
item["behind_3d"] = first.behind_3d;
item["points"] = points;
item["uvs"] = uvs;
item["colors"] = colors;
item["indices"] = indices;
item["glyph_count"] = count;
out.push_back(item);
i = end;
}
cached_frame_id = frame_id;
cached_commands = out;
return out;
}
bool Metin2PythonHost::has_3d_draws() { return !Render3DDraws().empty(); }
Ref<Image> Metin2PythonHost::memory_texture(const String &name, int64_t known_revision) {
UIMemoryTexture texture;
if (!UIRenderMemoryTexture(name.utf8().get_data(), &texture) || texture.width <= 0 || texture.height <= 0)
@@ -162,12 +305,107 @@ Color argb_color(std::uint32_t argb) {
((argb >> 24) & 255) / 255.0f);
}
bool read_pack_texture(const std::string &vpath, std::vector<std::uint8_t> &bytes) {
if (vpath.empty() || !mtpack40250::ready())
return false;
std::string norm = vpath;
for (char &ch : norm)
if (ch == '\\')
ch = '/';
std::string stripped = norm;
if (stripped.size() >= 2 && stripped[1] == ':')
stripped = stripped.substr(2);
while (!stripped.empty() && stripped.front() == '/')
stripped.erase(stripped.begin());
auto lower = [](std::string s) {
for (char &ch : s)
if (ch >= 'A' && ch <= 'Z')
ch = static_cast<char>(ch - 'A' + 'a');
return s;
};
for (const std::string &candidate : {
norm, stripped, "d:/" + stripped,
lower(norm), lower(stripped), lower("d:/" + stripped)}) {
if (mtpack40250::read(candidate, bytes) && !bytes.empty())
return true;
}
return false;
}
} // namespace
Array Metin2PythonHost::render3d_draws() {
struct CachedGeometry {
std::uint64_t revision;
std::uint64_t last_used;
Dictionary arrays;
};
static std::unordered_map<std::uint64_t, CachedGeometry> geometry_cache;
static std::uint64_t extraction = 0;
++extraction;
const auto &draws = Render3DDraws();
static bool capture_written = false;
if (!capture_written) {
const char *capture_path = std::getenv("MT_NATIVE_CAPTURE_PATH");
const char *minimum_text = std::getenv("MT_NATIVE_CAPTURE_MIN_DRAWS");
const unsigned long minimum = minimum_text ? std::strtoul(minimum_text, nullptr, 10) : 100UL;
if (capture_path && *capture_path && draws.size() >= minimum) {
capture_written = true;
try {
std::unordered_map<std::string, std::vector<std::uint8_t>> textures;
auto add_texture = [&](const std::string &name) {
if (name.empty() || textures.count(name))
return;
if (name.rfind("mem:", 0) == 0) {
UIMemoryTexture mem_tex;
if (UIRenderMemoryTexture(name, &mem_tex) && mem_tex.width > 0 && mem_tex.height > 0) {
auto mtra = native_draw_capture::encode_raw_argb_as_mtra(
static_cast<std::uint32_t>(mem_tex.width),
static_cast<std::uint32_t>(mem_tex.height),
mem_tex.argb.data());
if (!mtra.empty())
textures.emplace(name, std::move(mtra));
}
return;
}
std::vector<std::uint8_t> bytes;
if (read_pack_texture(name, bytes))
textures.emplace(name, std::move(bytes));
};
for (const Render3DDraw &draw : draws) {
add_texture(draw.texture0);
add_texture(draw.texture1);
}
const auto &ui_commands = UIRenderCommands();
for (const UIRenderCommand &cmd : ui_commands) {
if (cmd.kind == UIRenderCommand::Image) {
add_texture(cmd.text);
add_texture(cmd.mask);
}
}
unsigned ui_w = 0, ui_h = 0;
UIRenderGetSize(&ui_w, &ui_h);
native_draw_capture::write(
capture_path, draws, textures,
ui_w ? ui_w : 960u, ui_h ? ui_h : 640u, ui_commands);
UtilityFunctions::print(
"native draw capture: ", capture_path,
" draws=", static_cast<int64_t>(draws.size()),
" ui_commands=", static_cast<int64_t>(ui_commands.size()),
" textures=", static_cast<int64_t>(textures.size()));
} catch (const std::exception &error) {
UtilityFunctions::printerr("native draw capture failed: ", error.what());
}
}
}
Array out;
for (const Render3DDraw &draw : Render3DDraws()) {
out.resize(static_cast<int64_t>(draws.size()));
int64_t draw_index = 0;
for (const Render3DDraw &draw : draws) {
Dictionary item;
item["geometry_key"] = static_cast<int64_t>(draw.geometry_key);
item["geometry_revision"] = static_cast<int64_t>(draw.geometry_revision);
item["world"] = floats(draw.world, 16);
item["view"] = floats(draw.view, 16);
item["proj"] = floats(draw.proj, 16);
@@ -176,43 +414,73 @@ Array Metin2PythonHost::render3d_draws() {
item["pretransformed"] = draw.pretransformed;
item["lines"] = draw.lines;
PackedVector3Array positions;
positions.resize(static_cast<int64_t>(draw.positions.size() / 3));
for (int64_t i = 0; i < positions.size(); ++i)
positions[i] = Vector3(draw.positions[i * 3], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2]);
item["positions"] = positions;
if (!draw.rhw.empty())
item["rhw"] = floats(draw.rhw.data(), static_cast<int>(draw.rhw.size()));
if (!draw.normals.empty()) {
PackedVector3Array normals;
normals.resize(static_cast<int64_t>(draw.normals.size() / 3));
for (int64_t i = 0; i < normals.size(); ++i)
normals[i] = Vector3(draw.normals[i * 3], draw.normals[i * 3 + 1], draw.normals[i * 3 + 2]);
item["normals"] = normals;
Dictionary geometry;
if (draw.geometry_key != 0) {
auto cached = geometry_cache.find(draw.geometry_key);
if (cached != geometry_cache.end() && cached->second.revision == draw.geometry_revision) {
cached->second.last_used = extraction;
geometry = cached->second.arrays;
}
}
auto uvs = [](const std::vector<float> &values) {
if (geometry.is_empty()) {
PackedVector3Array positions;
positions.resize(static_cast<int64_t>(draw.positions.size() / 3));
if constexpr (sizeof(Vector3) == sizeof(float) * 3 && std::is_trivially_copyable_v<Vector3>) {
if (!draw.positions.empty())
std::memcpy(positions.ptrw(), draw.positions.data(), draw.positions.size() * sizeof(float));
} else {
Vector3 *dst = positions.ptrw();
for (int64_t i = 0; i < positions.size(); ++i)
dst[i] = Vector3(draw.positions[i * 3], draw.positions[i * 3 + 1], draw.positions[i * 3 + 2]);
}
geometry["positions"] = positions;
if (!draw.rhw.empty())
geometry["rhw"] = floats(draw.rhw.data(), static_cast<int>(draw.rhw.size()));
if (!draw.normals.empty()) {
PackedVector3Array normals;
normals.resize(static_cast<int64_t>(draw.normals.size() / 3));
if constexpr (sizeof(Vector3) == sizeof(float) * 3 && std::is_trivially_copyable_v<Vector3>) {
std::memcpy(normals.ptrw(), draw.normals.data(), draw.normals.size() * sizeof(float));
} else {
Vector3 *dst = normals.ptrw();
for (int64_t i = 0; i < normals.size(); ++i)
dst[i] = Vector3(draw.normals[i * 3], draw.normals[i * 3 + 1], draw.normals[i * 3 + 2]);
}
geometry["normals"] = normals;
}
auto uvs = [](const std::vector<float> &values) {
PackedVector2Array out;
out.resize(static_cast<int64_t>(values.size() / 2));
for (int64_t i = 0; i < out.size(); ++i)
out[i] = Vector2(values[i * 2], values[i * 2 + 1]);
if constexpr (sizeof(Vector2) == sizeof(float) * 2 && std::is_trivially_copyable_v<Vector2>) {
if (!values.empty())
std::memcpy(out.ptrw(), values.data(), values.size() * sizeof(float));
} else {
Vector2 *dst = out.ptrw();
for (int64_t i = 0; i < out.size(); ++i)
dst[i] = Vector2(values[i * 2], values[i * 2 + 1]);
}
return out;
};
if (!draw.uv0.empty())
item["uv0"] = uvs(draw.uv0);
if (!draw.uv1.empty())
item["uv1"] = uvs(draw.uv1);
if (!draw.diffuse.empty()) {
PackedColorArray colors;
colors.resize(static_cast<int64_t>(draw.diffuse.size()));
for (int64_t i = 0; i < colors.size(); ++i)
colors[i] = argb_color(draw.diffuse[i]);
item["diffuse"] = colors;
if (!draw.uv0.empty())
geometry["uv0"] = uvs(draw.uv0);
if (!draw.uv1.empty())
geometry["uv1"] = uvs(draw.uv1);
if (!draw.diffuse.empty()) {
PackedColorArray colors;
colors.resize(static_cast<int64_t>(draw.diffuse.size()));
for (int64_t i = 0; i < colors.size(); ++i)
colors[i] = argb_color(draw.diffuse[i]);
geometry["diffuse"] = colors;
}
PackedInt32Array indices;
indices.resize(static_cast<int64_t>(draw.indices.size()));
for (int64_t i = 0; i < indices.size(); ++i)
indices[i] = static_cast<int32_t>(draw.indices[i]);
geometry["indices"] = indices;
if (draw.geometry_key != 0)
geometry_cache[draw.geometry_key] = {draw.geometry_revision, extraction, geometry};
}
PackedInt32Array indices;
indices.resize(static_cast<int64_t>(draw.indices.size()));
for (int64_t i = 0; i < indices.size(); ++i)
indices[i] = static_cast<int32_t>(draw.indices[i]);
item["indices"] = indices;
item.merge(geometry);
item["alpha_blend"] = static_cast<int64_t>(draw.alpha_blend);
item["src_blend"] = static_cast<int64_t>(draw.src_blend);
@@ -226,6 +494,16 @@ Array Metin2PythonHost::render3d_draws() {
item["z_func"] = static_cast<int64_t>(draw.z_func);
item["lighting"] = static_cast<int64_t>(draw.lighting);
item["texture_factor"] = static_cast<int64_t>(draw.texture_factor);
bool uses_tf = false;
for (int s = 0; s < 2; ++s) {
if (draw.color_op[s] > 1 &&
(((draw.color_arg1[s] & 0xF) == 3) || ((draw.color_arg2[s] & 0xF) == 3)))
uses_tf = true;
if (draw.alpha_op[s] > 1 &&
(((draw.alpha_arg1[s] & 0xF) == 3) || ((draw.alpha_arg2[s] & 0xF) == 3)))
uses_tf = true;
}
item["uses_tf"] = uses_tf;
item["fog_enable"] = static_cast<int64_t>(draw.fog_enable);
item["color_op"] = static_cast<int64_t>(draw.color_op[0]);
item["alpha_op"] = static_cast<int64_t>(draw.alpha_op[0]);
@@ -243,7 +521,22 @@ Array Metin2PythonHost::render3d_draws() {
item["light0_ambient"] = Color(draw.light0_ambient[0], draw.light0_ambient[1], draw.light0_ambient[2],
draw.light0_ambient[3]);
item["ambient"] = argb_color(draw.ambient);
out.push_back(item);
PackedFloat32Array vp;
vp.resize(4);
vp[0] = draw.viewport[0];
vp[1] = draw.viewport[1];
vp[2] = draw.viewport[2];
vp[3] = draw.viewport[3];
item["viewport"] = vp;
out[draw_index++] = item;
}
if (extraction % 120 == 0) {
for (auto it = geometry_cache.begin(); it != geometry_cache.end();) {
if (extraction - it->second.last_used > 120)
it = geometry_cache.erase(it);
else
++it;
}
}
return out;
}
@@ -314,11 +607,14 @@ bool Metin2PythonHost::is_app_looping() { return false; }
void Metin2PythonHost::set_ui_size(int, int) {}
void Metin2PythonHost::ui_mouse_move(int, int) {}
void Metin2PythonHost::ui_mouse_button(int, bool, int, int) {}
void Metin2PythonHost::ui_mouse_wheel(int) {}
void Metin2PythonHost::ui_key(int, bool) {}
void Metin2PythonHost::ui_char(int) {}
void Metin2PythonHost::ui_ime_key(int) {}
void Metin2PythonHost::ui_update() {}
Array Metin2PythonHost::ui_render_commands() { return Array(); }
Array Metin2PythonHost::ui_render_commands_batched() { return Array(); }
bool Metin2PythonHost::has_3d_draws() { return false; }
Ref<Image> Metin2PythonHost::memory_texture(const String &, int64_t) { return Ref<Image>(); }
Array Metin2PythonHost::render3d_draws() { return Array(); }
Array Metin2PythonHost::audio_commands() { return Array(); }
@@ -341,11 +637,14 @@ void Metin2PythonHost::_bind_methods() {
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("set_ui_size", "width", "height"), &Metin2PythonHost::set_ui_size);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_move", "x", "y"), &Metin2PythonHost::ui_mouse_move);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_button", "button", "pressed", "x", "y"), &Metin2PythonHost::ui_mouse_button);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_mouse_wheel", "delta"), &Metin2PythonHost::ui_mouse_wheel);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_key", "key", "pressed"), &Metin2PythonHost::ui_key);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_char", "codepoint"), &Metin2PythonHost::ui_char);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_ime_key", "vkey"), &Metin2PythonHost::ui_ime_key);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_update"), &Metin2PythonHost::ui_update);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands"), &Metin2PythonHost::ui_render_commands);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("ui_render_commands_batched"), &Metin2PythonHost::ui_render_commands_batched);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("has_3d_draws"), &Metin2PythonHost::has_3d_draws);
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("memory_texture", "name", "known_revision"),
&Metin2PythonHost::memory_texture, DEFVAL(-1));
ClassDB::bind_static_method("Metin2PythonHost", D_METHOD("render3d_draws"), &Metin2PythonHost::render3d_draws);
+4
View File
@@ -38,12 +38,16 @@ public:
static void set_ui_size(int width, int height);
static void ui_mouse_move(int x, int y);
static void ui_mouse_button(int button, bool pressed, int x, int y);
static void ui_mouse_wheel(int delta);
static void ui_key(int key, bool pressed);
// WM_CHAR (Unicode code point) and WM_KEYDOWN (Win32 VK code) for the 40250 IME.
static void ui_char(int codepoint);
static void ui_ime_key(int vkey);
static void ui_update();
static godot::Array ui_render_commands();
// Ordered UI commands with consecutive, unclipped font quads packed into triangle arrays.
static godot::Array ui_render_commands_batched();
static bool has_3d_draws();
// The pixels of a "mem:<id>@<revision>" image command (the CGraphicFontTexture glyph pages) as an
// RGBA8 Image; null when the texture is gone or its revision is still `known_revision`.
static godot::Ref<godot::Image> memory_texture(const godot::String &name, int64_t known_revision);
+43 -6
View File
@@ -14,7 +14,11 @@
#include <asset_resolver.h>
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <unordered_map>
#include <utility>
using namespace godot;
@@ -72,14 +76,23 @@ Ref<Shader> terrain_shader() {
// DDS -> RGBA8 Image,resize 到 size×size。缓存(非函数静态 —— 见 cleanup)。
std::unordered_map<std::string, Ref<godot::Image>> g_layer_cache;
Ref<godot::Image> layer_image(const std::string &real_path, int size) {
std::unordered_map<std::string, std::pair<int, int>> g_layer_dimensions;
Ref<godot::Image> layer_image(const std::string &real_path, int size,
std::unordered_map<std::string, mtgodot::Image> &decoded_sources) {
auto &cache = g_layer_cache;
std::string key = real_path + "@" + std::to_string(size);
auto it = cache.find(key);
if (it != cache.end())
return it->second;
Ref<godot::Image> out;
mtgodot::Image d = mtgodot::dds_from_file(godot::String(real_path.c_str()));
mtgodot::Image d;
auto decoded = decoded_sources.find(real_path);
if (decoded != decoded_sources.end()) {
d = std::move(decoded->second);
decoded_sources.erase(decoded);
} else {
d = mtgodot::dds_from_file(godot::String(real_path.c_str()));
}
if (d.ok()) {
PackedByteArray b;
b.resize((int64_t)d.rgba.size());
@@ -120,25 +133,38 @@ Ref<ImageTexture> weight_tex(const std::vector<const fmt::SplatLayer *> &four) {
void cleanup_terrain_shader() {
g_terrain_shader.unref();
g_layer_cache.clear(); // 释放缓存的 Ref<Image>,别拖到 __cxa_finalize(那时引擎已析构)
g_layer_dimensions.clear();
}
Ref<ShaderMaterial> build_chunk_terrain_material(const fmt::SplatSet &splat,
const fmt::TextureSet &tset, const fmt::AssetResolver &res,
const String &shadowmap_path) {
const bool profile = std::getenv("MT_PROFILE_MAP") != nullptr;
const auto now = [] { return std::chrono::steady_clock::now(); };
const auto start = now();
// Texture2DArray 要求各 slice 同尺寸 —— 取用到的图层里的最大源边长(上限 1024),
// 只放大不缩小最大源,避免把 512² 地表贴图硬降采样(PARITY-GAP §3.4)。
int src_max = 256;
// Keep newly decoded sources only until the image pass consumes them. The size pass and image pass
// used to decode the same DDS twice on its first use.
std::unordered_map<std::string, mtgodot::Image> decoded_sources;
for (const auto &L : splat.layers) {
if (L.layer >= 1 && L.layer <= (int)tset.layers.size()) {
std::string rp = res.resolve(tset.layers[L.layer - 1].texture, nullptr);
if (rp.empty())
continue;
mtgodot::Image d = mtgodot::dds_from_file(godot::String(rp.c_str()));
if (d.ok())
src_max = std::max<int>(src_max, std::max<int>(d.w, d.h));
auto it = g_layer_dimensions.find(rp);
if (it == g_layer_dimensions.end()) {
mtgodot::Image d = mtgodot::dds_from_file(godot::String(rp.c_str()));
it = g_layer_dimensions.emplace(rp, d.ok() ? std::make_pair(int(d.w), int(d.h))
: std::make_pair(0, 0)).first;
decoded_sources.emplace(rp, std::move(d));
}
src_max = std::max(src_max, std::max(it->second.first, it->second.second));
}
}
const int LSIZE = std::min(1024, src_max);
const auto dimensions_done = now();
int n = std::min<int>(MAX_LAYERS, (int)splat.layers.size());
if (n == 0)
@@ -167,7 +193,7 @@ Ref<ShaderMaterial> build_chunk_terrain_material(const fmt::SplatSet &splat,
const fmt::TextureLayer &tl = tset.layers[L.layer - 1];
std::string rp = res.resolve(tl.texture, nullptr);
if (!rp.empty())
img = layer_image(rp, LSIZE);
img = layer_image(rp, LSIZE, decoded_sources);
// 原客户端 TextureSet.cpp:185:u' = (TexCoordBase*UScale)*vtx_cm + UOffset,
// TexCoordBase = 1/(PATCH_XSIZE*CELLSCALE) = 1/3200;区块归一化 UV -> 平铺频率 = 8*Scale。
float us = tl.u_scale > 0.01f ? tl.u_scale : 1.0f;
@@ -176,10 +202,12 @@ Ref<ShaderMaterial> build_chunk_terrain_material(const fmt::SplatSet &splat,
}
imgs.push_back(img.is_valid() ? img : fallback);
}
const auto images_done = now();
Ref<Texture2DArray> arr;
arr.instantiate();
arr->create_from_images(imgs);
const auto array_done = now();
// 权重贴图:ceil(n/4) 张 RGBA8(每通道一层 alpha)
Ref<ImageTexture> wtex[4];
@@ -192,6 +220,7 @@ Ref<ShaderMaterial> build_chunk_terrain_material(const fmt::SplatSet &splat,
}
wtex[g] = weight_tex(grp);
}
const auto weights_done = now();
Ref<ShaderMaterial> mat;
mat.instantiate();
@@ -209,6 +238,7 @@ Ref<ShaderMaterial> build_chunk_terrain_material(const fmt::SplatSet &splat,
mat->set_shader_parameter("layer_uv", uva);
}
const auto shader_done = now();
if (!shadowmap_path.is_empty()) {
mtgodot::Image sm = mtgodot::dds_from_file(shadowmap_path);
if (sm.ok()) {
@@ -221,6 +251,13 @@ Ref<ShaderMaterial> build_chunk_terrain_material(const fmt::SplatSet &splat,
mat->set_shader_parameter("use_shadowmap", true);
}
}
if (profile) {
const auto ms = [](auto a, auto b) { return std::chrono::duration<double, std::milli>(b - a).count(); };
std::fprintf(stderr, "MATERIAL_PROFILE dimensions=%.3f images=%.3f array=%.3f weights=%.3f shader=%.3f shadow=%.3f total=%.3f layers=%d size=%d\n",
ms(start, dimensions_done), ms(dimensions_done, images_done), ms(images_done, array_done),
ms(array_done, weights_done), ms(weights_done, shader_done), ms(shader_done, now()),
ms(start, now()), n, LSIZE);
}
return mat;
}
@@ -13,6 +13,7 @@
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace mtnet::classic;
@@ -23,6 +24,16 @@ constexpr std::uint32_t kHandshake = 0x2468ace0;
constexpr int kHandshakeRetryLimit = 32; // game/src/desc.h HANDSHAKE_RETRY_LIMIT
constexpr int kIdleMs = 20000;
int fake_mob_count()
{
const char* value = std::getenv("MT_FAKE_MOB_COUNT");
if (!value || !*value)
return 1;
char* end = nullptr;
const long count = std::strtol(value, &end, 10);
return *end == '\0' && count >= 1 && count <= 64 ? static_cast<int>(count) : 1;
}
// get_dword_time(): milliseconds on the server's clock.
std::uint32_t now_ms()
{
@@ -582,6 +593,20 @@ bool FakeLoginServer::ServeGame(Connection& c, int index)
mob_update.attack_speed = 100;
if (!c.Send(mob) || !c.Send(mob_update))
return Fail(name + ": send monster");
// Optional crowd for render profiling. Keep the primary dog and its click path clear so the
// existing combat test still attacks kMobVID; additional dogs are passive scenery.
const int mob_count = fake_mob_count();
for (int i = 1; i < mob_count; ++i)
{
GCCharacterAdd extra = mob;
extra.vid = kMobVID + static_cast<std::uint32_t>(i);
extra.x = kMobX - 350 - ((i - 1) % 8) * 170;
extra.y = kMobY + 250 + ((i - 1) / 8) * 190;
GCCharacterUpdate extra_update = mob_update;
extra_update.vid = extra.vid;
if (!c.Send(extra) || !c.Send(extra_update))
return Fail(name + ": send extra monster");
}
// An NPC is ADD + ADDITIONAL_INFO + UPDATE like a PC (the name is the mob_proto locale name).
GCCharacterAdd keeper = zeroed<GCCharacterAdd>();
keeper.header = HDR_GC_CHARACTER_ADD;
+60 -3
View File
@@ -49,10 +49,12 @@
#include "GameLib/FlyingData.h"
#include "GameLib/FlyingObjectManager.h"
#include "EffectLib/EffectManager.h"
#include "EffectLib/EffectInstance.h"
#include "EterLib/Camera.h"
#include "GameLib/ItemManager.h"
#include "UserInterface/PythonItem.h"
#include "UserInterface/StdAfx.h"
#include "UserInterface/PythonPlayer.h"
#include "UserInterface/PythonExchange.h"
#include "UserInterface/PythonTextTail.h"
#include "UserInterface/PythonCharacterManager.h"
@@ -674,11 +676,11 @@ int main(int argc, char** argv)
const size_t moves_before = server ? server->Events().size() : 0;
if (main_instance)
main_instance->NEW_GetPixelPosition(&start);
PythonBoot::UIMouseButton(1, true, 400, 520);
PythonBoot::UIMouseButton(1, true, 400, 220);
pump_until(0.1, [] { return false; });
PythonBoot::UIMouseButton(1, false, 400, 520);
PythonBoot::UIMouseButton(1, false, 400, 220);
CHECK(main_instance && pump_until(3, [&] { return walked() > 100.0f; }));
CHECK(main_instance && pump_until(5, [&] { return !main_instance->IsWalking(); }));
CHECK(main_instance && pump_until(10, [&] { return !main_instance->IsWalking(); }));
std::printf("port_login_flow_test: click walked %.0f cm\n", main_instance ? walked() : 0.0f);
if (server)
{
@@ -746,6 +748,61 @@ int main(int argc, char** argv)
++attacks;
std::printf("port_login_flow_test: %zu attack(s) until the stray dog died\n", attacks);
CHECK(attacks >= (size_t) FakeLoginServer::kMobHits);
// Camera mouse wheel zoom test:
// Test zooming in (Wheel UP, positive delta) and zooming out (Wheel DOWN, negative delta).
{
CCamera* pkCmrCur = CCameraManager::Instance().GetCurrentCamera();
CHECK(pkCmrCur != nullptr);
if (pkCmrCur)
{
const float initial_dist = pkCmrCur->GetDistance();
std::printf("port_login_flow_test: initial camera distance = %f\n", initial_dist);
// Zoom IN: positive wheel delta
PythonBoot::UIMouseWheel(120);
pump_until(0.1, [] { return false; });
const float zoomed_in_dist = pkCmrCur->GetDistance();
std::printf("port_login_flow_test: zoomed in camera distance = %f\n", zoomed_in_dist);
CHECK(zoomed_in_dist < initial_dist);
// Zoom OUT: negative wheel delta
PythonBoot::UIMouseWheel(-240);
pump_until(0.1, [] { return false; });
const float zoomed_out_dist = pkCmrCur->GetDistance();
std::printf("port_login_flow_test: zoomed out camera distance = %f\n", zoomed_out_dist);
CHECK(zoomed_out_dist > zoomed_in_dist);
}
}
// P-grade skill test: verify P-grade skill (geomgyeong / Aura of the Sword) uses Grade 3 motion (geomgyeong_4.msa),
// spawns effect instances (geom_4_badak.mse, geom_4_sword_making.mse), and renders them.
{
CPythonPlayer& rkPlayer = CPythonPlayer::Instance();
rkPlayer.SetStatus(POINT_SP, 1000);
rkPlayer.SetStatus(POINT_MAX_SP, 1000);
rkPlayer.SetStatus(POINT_HP, 1000);
rkPlayer.SetStatus(POINT_MAX_HP, 1000);
CInstanceBase* pkInstMain = rkPlayer.NEW_GetMainActorPtr();
CHECK(pkInstMain != nullptr);
if (pkInstMain)
{
pkInstMain->ChangeWeapon(19);
rkPlayer.SetSkill(4, 4); // slot 4 = geomgyeong
rkPlayer.SetSkillLevel_(4, 3, 40); // Grade 3 (P), Level 40
CHECK(rkPlayer.GetSkillGrade(4) == 3);
rkPlayer.ClickSkillSlot(4);
CHECK(pkInstMain->IsUsingSkill());
bool has_effects = false;
for (int step = 0; step < 10; ++step)
{
pump_until(0.1, [] { return false; });
if (CEffectInstance::GetRenderingEffectCount() > 0)
has_effects = true;
}
CHECK(has_effects);
}
}
}
}
// 10. (批次 4-c) NPC shop: a click on the general store walks up to it (__OnClickActor) and sends CG_ON_CLICK;