feat: complete native client rendering parity updates
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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],¢er);
|
||||
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;
|
||||
}
|
||||
@@ -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 ¢er, 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,§);
|
||||
|
||||
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,§);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 ¢er,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 ¢er, 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,§);
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user