fix: .gitignore + add 44 language JSON files

This commit is contained in:
shuiyu1123 committed 2026-07-14 01:17:33 +08:00
1 parent e8fd68e27b
commit 8de545dd2c
227 files changed
+22603

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
#pragma once
// ============================================================================
// mesh.h - 网格 (顶点缓冲 + 索引缓冲)
// ============================================================================
#include <d3d11.h>
#include <stdint.h>
typedef struct {
ID3D11Buffer* vertex_buffer;
ID3D11Buffer* index_buffer;
uint32_t vertex_count;
uint32_t index_count;
uint32_t vertex_stride;
} MeshBuffer;
// 从内存数据创建网格
MeshBuffer* mesh_create(ID3D11Device* device,
const void* vertices, uint32_t vertex_count, uint32_t vertex_stride,
const uint32_t* indices, uint32_t index_count);
// 从 OBJ 文件加载网格 (简化版: 仅顶点+面)
MeshBuffer* mesh_load_obj(ID3D11Device* device, const char* filepath);
// 销毁
void mesh_destroy(MeshBuffer* mesh);
// 绘制
void mesh_draw(ID3D11DeviceContext* ctx, MeshBuffer* mesh);
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <d3d11.h>
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
uint32_t width, height;
} Texture;
Texture* texture_load_bmp(ID3D11Device* dev, const char* path);
Texture* texture_create_white(ID3D11Device* dev, uint32_t w, uint32_t h);
void texture_destroy(Texture* tex);
+192
View File
@@ -0,0 +1,192 @@
#include "asset_manager.h"
#include "../Rendering/mesh.h"
#include "../Rendering/texture.h"
#include "../Rendering/shader.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ASSET_BUCKET_COUNT 256
struct AssetManager {
Asset* buckets[ASSET_BUCKET_COUNT];
uint32_t next_id;
};
static uint32_t hash_str(const char* s) {
uint32_t h = 5381;
while (*s) h = ((h << 5) + h) + (unsigned char)*s++;
return h % ASSET_BUCKET_COUNT;
}
static int str_ends_with(const char* s, const char* suffix) {
size_t ls = strlen(s), lf = strlen(suffix);
return ls >= lf && strcmp(s + ls - lf, suffix) == 0;
}
static Asset* asset_alloc(AssetManager* am, AssetType type, const char* path, void* data, void (*destroy)(void*)) {
Asset* a = (Asset*)calloc(1, sizeof(Asset));
a->id = ++am->next_id;
a->type = type;
a->refcount = 1;
a->owner = am;
strncpy(a->path, path, sizeof(a->path) - 1);
a->data = data;
a->destroy = destroy;
uint32_t h = hash_str(path);
a->next = am->buckets[h];
am->buckets[h] = a;
return a;
}
static void asset_free(AssetManager* am, Asset* a) {
if (!a) return;
uint32_t h = hash_str(a->path);
Asset** cur = &am->buckets[h];
while (*cur) {
if (*cur == a) {
*cur = a->next;
break;
}
cur = &(*cur)->next;
}
if (a->destroy) a->destroy(a->data);
free(a);
}
AssetManager* asset_manager_create(void) {
AssetManager* am = (AssetManager*)calloc(1, sizeof(AssetManager));
return am;
}
void asset_manager_destroy(AssetManager* am) {
if (!am) return;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
Asset* a = am->buckets[i];
while (a) {
Asset* next = a->next;
if (a->destroy) a->destroy(a->data);
free(a);
a = next;
}
}
free(am);
}
Asset* asset_manager_find(AssetManager* am, const char* path) {
if (!am || !path) return NULL;
uint32_t h = hash_str(path);
for (Asset* a = am->buckets[h]; a; a = a->next) {
if (strcmp(a->path, path) == 0) return a;
}
return NULL;
}
Asset* asset_manager_get(AssetManager* am, uint32_t id) {
if (!am || id == 0) return NULL;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
for (Asset* a = am->buckets[i]; a; a = a->next) {
if (a->id == id) return a;
}
}
return NULL;
}
void* asset_data(Asset* a) { return a ? a->data : NULL; }
void asset_acquire(Asset* a) { if (a) a->refcount++; }
void asset_release(Asset* a) {
if (!a) return;
a->refcount--;
if (a->refcount <= 0 && a->owner) {
asset_free(a->owner, a);
}
}
// ------------------------------------------------------------------
// Mesh
// ------------------------------------------------------------------
static void destroy_mesh_asset(void* data) { mesh_destroy((Mesh*)data); }
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Mesh* mesh = NULL;
if (strcmp(path, "cube") == 0) mesh = mesh_create_cube(dev);
else if (strcmp(path, "sphere") == 0) mesh = mesh_create_sphere(dev, 1.0f, 16);
else if (strcmp(path, "plane") == 0) mesh = mesh_create_plane(dev, 10.0f);
else if (str_ends_with(path, ".obj")) mesh = mesh_load_obj(dev, path);
if (!mesh) return NULL;
return asset_alloc(am, ASSET_MESH, path, mesh, destroy_mesh_asset);
}
// ------------------------------------------------------------------
// Texture
// ------------------------------------------------------------------
static void destroy_texture_asset(void* data) { texture_destroy((Texture*)data); }
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Texture* tex = NULL;
if (strcmp(path, "checkerboard") == 0) tex = texture_create_checkerboard(dev, 256, 8);
else tex = texture_load_from_file(dev, path);
if (!tex) return NULL;
return asset_alloc(am, ASSET_TEXTURE, path, tex, destroy_texture_asset);
}
// ------------------------------------------------------------------
// Shader
// ------------------------------------------------------------------
static void destroy_shader_asset(void* data) { shader_destroy((Shader*)data); }
static char* read_file(const char* path, uint32_t* out_len) {
FILE* f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size <= 0) { fclose(f); return NULL; }
char* buf = (char*)malloc((size_t)size + 1);
if (!buf) { fclose(f); return NULL; }
fread(buf, 1, (size_t)size, f);
buf[size] = '\0';
fclose(f);
if (out_len) *out_len = (uint32_t)size;
return buf;
}
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path) {
if (!am || !dev) return NULL;
char key[512];
if (vs_path && ps_path) snprintf(key, sizeof(key), "%s|%s", vs_path, ps_path);
else strcpy(key, "default");
Asset* existing = asset_manager_find(am, key);
if (existing) { asset_acquire(existing); return existing; }
Shader* s = shader_create();
if (vs_path && ps_path) {
uint32_t vs_len = 0, ps_len = 0;
char* vs = read_file(vs_path, &vs_len);
char* ps = read_file(ps_path, &ps_len);
if (!vs || !ps || !shader_compile(s, dev, vs, ps, vs_len, ps_len)) {
shader_destroy(s); free(vs); free(ps); return NULL;
}
free(vs); free(ps);
} else {
if (!shader_compile(s, dev, NULL, NULL, 0, 0)) {
shader_destroy(s); return NULL;
}
}
return asset_alloc(am, ASSET_SHADER, key, s, destroy_shader_asset);
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
#include <d3d11.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AssetManager AssetManager;
typedef struct Asset Asset;
typedef enum {
ASSET_MESH = 0,
ASSET_TEXTURE,
ASSET_SHADER,
ASSET_MATERIAL,
ASSET_COUNT
} AssetType;
struct Asset {
uint32_t id;
AssetType type;
int refcount;
char path[256];
void* data;
void (*destroy)(void* data);
Asset* next;
struct AssetManager* owner;
};
AssetManager* asset_manager_create(void);
void asset_manager_destroy(AssetManager* am);
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path);
Asset* asset_manager_find(AssetManager* am, const char* path);
Asset* asset_manager_get(AssetManager* am, uint32_t id);
void* asset_data(Asset* a);
void asset_acquire(Asset* a);
void asset_release(Asset* a);
#ifdef __cplusplus
}
#endif
+137
View File
@@ -0,0 +1,137 @@
; ============================================================================
; asm_macros.inc - MASM 通用宏定义
; Windows x64 调用约定: RCX, RDX, R8, R9 传参, XMM0-XMM3 浮点
; ============================================================================
; --- 函数帧管理宏 ---
; PROLOGUE: 保存非易失寄存器, 分配栈空间
; 使用: PROLOGUE 栈空间大小
PROLOGUE MACRO stack_size:REQ
push rbx
push rbp
push rdi
push rsi
push r12
push r13
push r14
push r15
sub rsp, stack_size
; 保存浮点参数寄存器 (如果有)
movsd qword ptr [rsp + stack_size - 8], xmm6
movsd qword ptr [rsp + stack_size - 16], xmm7
ENDM
; EPILOGUE: 恢复非易失寄存器, 释放栈空间
EPILOGUE MACRO stack_size:REQ
movsd xmm6, qword ptr [rsp + stack_size - 8]
movsd xmm7, qword ptr [rsp + stack_size - 16]
add rsp, stack_size
pop r15
pop r14
pop r13
pop r12
pop rsi
pop rdi
pop rbp
pop rbx
ret
ENDM
; --- SSE/AVX 常量 ---
.DATA
ALIGN 16
sse_mask_abs DD 07FFFFFFFH, 07FFFFFFFH, 07FFFFFFFH, 07FFFFFFFH ; 绝对值掩码
sse_negate DD 80000000H, 80000000H, 80000000H, 80000000H ; 符号位掩码
sse_one DD 3F800000H, 3F800000H, 3F800000H, 3F800000H ; 1.0f
sse_half DD 3F000000H, 3F000000H, 3F000000H, 3F000000H ; 0.5f
eps_float DD 33D6BF94H ; 1e-6f
.CODE
; --- 工具宏 ---
; LOAD_VEC3: 从内存加载 Vec3 到 XMM 寄存器 (填充 w=1.0)
; ptr: 内存地址, reg: XMM 寄存器
LOAD_VEC3 MACRO ptr, reg
movss xmm0, dword ptr [ptr] ; x
movss xmm1, dword ptr [ptr + 4] ; y
movss xmm2, dword ptr [ptr + 8] ; z
unpcklps xmm0, xmm1 ; {x, y, ?, ?}
movss xmm3, dword ptr [sse_one] ; w = 1.0
unpcklps xmm2, xmm3 ; {z, 1.0, ?, ?}
movlhps xmm0, xmm2 ; {x, y, z, 1.0}
movaps reg, xmm0
ENDM
; STORE_VEC3: 将 XMM 寄存器的前3个float存储到内存
STORE_VEC3 MACRO reg, ptr
movss dword ptr [ptr], xmm0 ; x
shufps xmm0, xmm0, 01010101b
movss dword ptr [ptr + 4], xmm0 ; y
shufps xmm0, xmm0, 10101010b
movss dword ptr [ptr + 8], xmm0 ; z
ENDM
; --- 外部函数声明 ---
externdef vec3_add:PROC
externdef vec3_sub:PROC
externdef vec3_scale:PROC
externdef vec3_dot:PROC
externdef vec3_cross:PROC
externdef vec3_length:PROC
externdef vec3_normalize:PROC
externdef vec3_lerp:PROC
externdef vec3_distance:PROC
externdef vec4_add:PROC
externdef vec4_sub:PROC
externdef vec4_mul:PROC
externdef vec4_dot:PROC
externdef vec4_length:PROC
externdef vec4_normalize:PROC
externdef mat4_identity:PROC
externdef mat4_multiply:PROC
externdef mat4_transpose:PROC
externdef mat4_invert:PROC
externdef mat4_translate:PROC
externdef mat4_scale_f:PROC
externdef mat4_rotate_x:PROC
externdef mat4_rotate_y:PROC
externdef mat4_rotate_z:PROC
externdef mat4_perspective:PROC
externdef mat4_orthographic:PROC
externdef mat4_lookat:PROC
externdef quat_multiply:PROC
externdef quat_conjugate:PROC
externdef quat_normalize:PROC
externdef quat_rotate_vec3:PROC
externdef quat_slerp:PROC
externdef quat_to_matrix:PROC
externdef quat_from_axis_angle:PROC
externdef quat_from_euler:PROC
externdef mem_aligned_alloc:PROC
externdef mem_aligned_free:PROC
externdef mem_aligned_realloc:PROC
externdef fast_memcpy:PROC
externdef fast_memset:PROC
externdef fast_strlen:PROC
externdef fast_strcmp:PROC
externdef prof_rdtsc:PROC
externdef prof_cycles_to_ms:PROC
externdef prof_get_freq:PROC
; C 数学运行时
externdef cosf:PROC
externdef sinf:PROC
externdef tanf:PROC
; Windows API
externdef Sleep:PROC
externdef QueryPerformanceFrequency:PROC
externdef QueryPerformanceCounter:PROC
File diff suppressed because it is too large. Load diff
+79
View File
@@ -0,0 +1,79 @@
#pragma once
#include <math.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <Windows.h>
#if !defined(MATH_ASM_AVAILABLE) || MATH_ASM_AVAILABLE == 0
typedef struct { float x, y, z; } Vec3;
typedef struct { float x, y, z, w; } Vec4;
typedef struct { float m[4][4]; } Mat4;
typedef struct { float x, y, z, w; } Quat;
static inline void vec3_add(const Vec3* a, const Vec3* b, Vec3* o) { o->x=a->x+b->x; o->y=a->y+b->y; o->z=a->z+b->z; }
static inline void vec3_sub(const Vec3* a, const Vec3* b, Vec3* o) { o->x=a->x-b->x; o->y=a->y-b->y; o->z=a->z-b->z; }
static inline void vec3_scale(const Vec3* a, float s, Vec3* o) { o->x=a->x*s; o->y=a->y*s; o->z=a->z*s; }
static inline float vec3_dot(const Vec3* a, const Vec3* b) { return a->x*b->x+a->y*b->y+a->z*b->z; }
static inline void vec3_cross(const Vec3* a, const Vec3* b, Vec3* o) { o->x=a->y*b->z-a->z*b->y; o->y=a->z*b->x-a->x*b->z; o->z=a->x*b->y-a->y*b->x; }
static inline float vec3_length(const Vec3* a) { return sqrtf(a->x*a->x+a->y*a->y+a->z*a->z); }
static inline void vec3_normalize(const Vec3* a, Vec3* o) { float l=vec3_length(a); if(l<1e-6f){o->x=o->y=o->z=0;return;} float i=1.0f/l; o->x=a->x*i; o->y=a->y*i; o->z=a->z*i; }
static inline void vec3_lerp(const Vec3* a, const Vec3* b, float t, Vec3* o) { o->x=a->x+(b->x-a->x)*t; o->y=a->y+(b->y-a->y)*t; o->z=a->z+(b->z-a->z)*t; }
static inline float vec3_distance(const Vec3* a, const Vec3* b) { Vec3 d; vec3_sub(a,b,&d); return vec3_length(&d); }
static inline void mat4_identity(Mat4* o) { memset(o,0,sizeof(Mat4)); o->m[0][0]=o->m[1][1]=o->m[2][2]=o->m[3][3]=1.0f; }
static inline void mat4_multiply(const Mat4* a, const Mat4* b, Mat4* o) { Mat4 r; int i,j,k; for(i=0;i<4;i++) for(j=0;j<4;j++) { r.m[i][j]=0; for(k=0;k<4;k++) r.m[i][j]+=a->m[i][k]*b->m[k][j]; } *o=r; }
static inline void mat4_translate(float x, float y, float z, Mat4* o) { mat4_identity(o); o->m[3][0]=x; o->m[3][1]=y; o->m[3][2]=z; }
static inline void mat4_rotate_y(float angle, Mat4* o) { float c=cosf(angle),s=sinf(angle); mat4_identity(o); o->m[0][0]=c; o->m[0][2]=-s; o->m[2][0]=s; o->m[2][2]=c; }
static inline void mat4_perspective(float fov, float aspect, float nearP, float farP, Mat4* o) { float t=tanf(fov*0.5f); memset(o,0,sizeof(Mat4)); o->m[0][0]=1.0f/(aspect*t); o->m[1][1]=1.0f/t; o->m[2][2]=farP/(nearP-farP); o->m[2][3]=-1.0f; o->m[3][2]=(nearP*farP)/(nearP-farP); }
static inline void mat4_orthographic(float w, float h, float nearP, float farP, Mat4* o) { memset(o,0,sizeof(Mat4)); o->m[0][0]=2.0f/w; o->m[1][1]=2.0f/h; o->m[2][2]=1.0f/(nearP-farP); o->m[3][2]=nearP/(nearP-farP); o->m[3][3]=1.0f; }
static inline void mat4_lookat(const Vec3* eye, const Vec3* target, const Vec3* up, Mat4* o) { Vec3 f,r,u; vec3_sub(target,eye,&f); vec3_normalize(&f,&f); vec3_cross(up,&f,&r); vec3_normalize(&r,&r); vec3_cross(&f,&r,&u); mat4_identity(o); o->m[0][0]=r.x; o->m[0][1]=u.x; o->m[0][2]=-f.x; o->m[1][0]=r.y; o->m[1][1]=u.y; o->m[1][2]=-f.y; o->m[2][0]=r.z; o->m[2][1]=u.z; o->m[2][2]=-f.z; o->m[3][0]=-vec3_dot(&r,eye); o->m[3][1]=-vec3_dot(&u,eye); o->m[3][2]=vec3_dot(&f,eye); }
static inline void mat4_transpose(const Mat4* in, Mat4* out) { for (int i=0;i<4;i++) for (int j=0;j<4;j++) out->m[i][j]=in->m[j][i]; }
static inline void quat_conjugate_raw(const Quat* q, Quat* o) { o->x=-q->x; o->y=-q->y; o->z=-q->z; o->w=q->w; }
static inline void quat_multiply(const Quat* a, const Quat* b, Quat* o) { o->x=a->w*b->x+a->x*b->w+a->y*b->z-a->z*b->y; o->y=a->w*b->y-a->x*b->z+a->y*b->w+a->z*b->x; o->z=a->w*b->z+a->x*b->y-a->y*b->x+a->z*b->w; o->w=a->w*b->w-a->x*b->x-a->y*b->y-a->z*b->z; }
static inline void quat_normalize(const Quat* q, Quat* o) { float l=sqrtf(q->x*q->x+q->y*q->y+q->z*q->z+q->w*q->w); if(l<1e-6f){*o=*q;return;} float i=1.0f/l; o->x=q->x*i; o->y=q->y*i; o->z=q->z*i; o->w=q->w*i; }
static inline void quat_rotate_vec3(const Quat* q, const Vec3* v, Vec3* o) { Quat vq={v->x,v->y,v->z,0}; Quat qinv; quat_conjugate_raw(q,&qinv); Quat tmp; quat_multiply(q,&vq,&tmp); Quat result; quat_multiply(&tmp,&qinv,&result); o->x=result.x; o->y=result.y; o->z=result.z; }
static inline void quat_to_matrix(const Quat* q, Mat4* o) { float xx=q->x*q->x,yy=q->y*q->y,zz=q->z*q->z,xy=q->x*q->y,xz=q->x*q->z,yz=q->y*q->z,wx=q->w*q->x,wy=q->w*q->y,wz=q->w*q->z; mat4_identity(o); o->m[0][0]=1-2*(yy+zz); o->m[0][1]=2*(xy+wz); o->m[0][2]=2*(xz-wy); o->m[1][0]=2*(xy-wz); o->m[1][1]=1-2*(xx+zz); o->m[1][2]=2*(yz+wx); o->m[2][0]=2*(xz+wy); o->m[2][1]=2*(yz-wx); o->m[2][2]=1-2*(xx+yy); }
static inline void quat_from_euler(float x, float y, float z, Quat* o) {
float cx = cosf(x * 0.5f), sx = sinf(x * 0.5f);
float cy = cosf(y * 0.5f), sy = sinf(y * 0.5f);
float cz = cosf(z * 0.5f), sz = sinf(z * 0.5f);
o->w = cx * cy * cz + sx * sy * sz;
o->x = sx * cy * cz - cx * sy * sz;
o->y = cx * sy * cz + sx * cy * sz;
o->z = cx * cy * sz - sx * sy * cz;
}
static inline void quat_to_euler(const Quat* q, float* x, float* y, float* z) {
float sinr_cosp = 2.0f * (q->w * q->x + q->y * q->z);
float cosr_cosp = 1.0f - 2.0f * (q->x * q->x + q->y * q->y);
*x = atan2f(sinr_cosp, cosr_cosp);
float sinp = 2.0f * (q->w * q->y - q->z * q->x);
if (fabsf(sinp) >= 1.0f) *y = copysignf(1.57079633f, sinp);
else *y = asinf(sinp);
float siny_cosp = 2.0f * (q->w * q->z + q->x * q->y);
float cosy_cosp = 1.0f - 2.0f * (q->y * q->y + q->z * q->z);
*z = atan2f(siny_cosp, cosy_cosp);
}
static inline void* mem_aligned_alloc(uint64_t size, uint64_t alignment) { void* p; p = _aligned_malloc(size, alignment); return p; }
static inline void mem_aligned_free(void* ptr) { _aligned_free(ptr); }
static inline void fast_memcpy(void* dst, const void* src, uint64_t count) { memcpy(dst, src, (size_t)count); }
static inline void fast_memset(void* dst, int v, uint64_t count) { memset(dst, v, (size_t)count); }
static inline uint64_t fast_strlen(const char* s) { return strlen(s); }
static inline uint64_t prof_rdtsc(void) { LARGE_INTEGER f,t; QueryPerformanceFrequency(&f); QueryPerformanceCounter(&t); return (uint64_t)(t.QuadPart * 1000000000ULL / f.QuadPart); }
static inline double prof_cycles_to_ms(uint64_t c) { return (double)c / 1000000.0; }
static inline uint64_t prof_get_freq(void) { LARGE_INTEGER f; QueryPerformanceFrequency(&f); return (uint64_t)f.QuadPart; }
typedef struct { uint64_t start; uint64_t end; double elapsed_ms; } ProfTimer;
static inline void prof_timer_start(ProfTimer* t) { t->start = prof_rdtsc(); }
static inline void prof_timer_stop(ProfTimer* t) { t->end = prof_rdtsc(); t->elapsed_ms = prof_cycles_to_ms(t->end - t->start); }
#endif
+180
View File
@@ -0,0 +1,180 @@
; ============================================================================
; memory.asm - 对齐内存分配器 (x86-64 MASM)
; 支持 16/32/64 字节对齐 (SIMD 需要)
; ============================================================================
include asm_macros.inc
.DATA
ALIGN 16
_alloc_header_size EQU 32 ; 分配头大小 (包含对齐偏移和原始指针)
.CODE
; ============================================================================
; 对齐内存分配
; ============================================================================
; void* mem_aligned_alloc(uint64_t size, uint64_t alignment)
; RCX = size, RDX = alignment
; 返回: RAX = 对齐指针
mem_aligned_alloc PROC
push rbx
push rdi
push rsi
sub rsp, 40 ; 保留栈空间
mov rbx, rcx ; rbx = size
mov rdi, rdx ; rdi = alignment
; 验证 alignment 是 2 的幂
mov rax, rdi
dec rax
test rdi, rax
jnz alloc_invalid_alignment
; 确保 alignment >= 16
cmp rdi, 16
jae alloc_alignment_ok
mov rdi, 16
alloc_alignment_ok:
; 需要的总大小 = size + alignment + header
mov rax, rbx
add rax, rdi ; + alignment
add rax, _alloc_header_size ; + header
mov rsi, rax ; rsi = total size
; 调用 malloc
mov rcx, rsi
call malloc
test rax, rax
jz alloc_failed
; rax = 原始指针
; 计算对齐后的地址
mov rcx, rax
add rcx, _alloc_header_size ; 跳过 header
; 对齐
mov rdx, rdi
dec rdx
not rdx
add rcx, rdx ; 加上 alignment-1
and rcx, rdx ; 对齐到 alignment
; rcx = 对齐后的指针
; 在对齐指针前存储原始指针
mov [rcx - 8], rax ; 存储原始 malloc 指针
mov [rcx - 16], rbx ; 存储请求的 size
mov [rcx - 24], rdi ; 存储 alignment
mov [rcx - 32], rsi ; 存储 total size
mov rax, rcx ; 返回对齐指针
jmp alloc_done
alloc_invalid_alignment:
xor rax, rax ; 返回 NULL
jmp alloc_done
alloc_failed:
xor rax, rax
alloc_done:
add rsp, 40
pop rsi
pop rdi
pop rbx
ret
mem_aligned_alloc ENDP
; void mem_aligned_free(void* ptr)
; RCX = ptr (对齐指针)
mem_aligned_free PROC
test rcx, rcx
jz free_done
; 获取原始 malloc 指针
mov rax, [rcx - 8]
test rax, rax
jz free_done
; 调用 free
mov rcx, rax
call free
free_done:
ret
mem_aligned_free ENDP
; void* mem_aligned_realloc(void* ptr, uint64_t size, uint64_t alignment)
; RCX = ptr, RDX = size, R8 = alignment
; 返回: RAX = 新对齐指针
mem_aligned_realloc PROC
push rbx
push rdi
push rsi
push r12
sub rsp, 56 ; shadow + 3 saved qwords
mov rbx, rdx ; rbx = new size
mov rdi, r8 ; rdi = alignment
; 如果 ptr 为 NULL, 直接分配
test rcx, rcx
jz realloc_alloc
; 保存旧信息
mov [rsp + 32], rcx ; 旧对齐指针(即用户数据起始)
mov rax, [rcx - 8]
mov [rsp + 24], rax ; 旧原始 malloc 指针
mov rax, [rcx - 16]
mov [rsp + 16], rax ; 旧 size
; 分配新内存
mov rcx, rbx
mov rdx, rdi
call mem_aligned_alloc
test rax, rax
jz realloc_failed
mov rsi, rax ; rsi = 新对齐指针
; 复制旧数据 (取较小值)
mov rdx, [rsp + 16] ; 旧 size
cmp rdx, rbx
jbe realloc_copy_ok
mov rdx, rbx
realloc_copy_ok:
; fast_memcpy(dst, src, count): rcx=dst, rdx=src, r8=count
mov rcx, rsi
mov r8, rdx
mov rdx, [rsp + 32] ; 旧对齐指针
call fast_memcpy
; 释放旧内存(使用原始 malloc 指针)
mov rcx, [rsp + 24]
call mem_aligned_free
mov rax, rsi ; 返回新指针
jmp realloc_done
realloc_alloc:
mov rcx, rbx
mov rdx, rdi
call mem_aligned_alloc
jmp realloc_done
realloc_failed:
xor rax, rax
realloc_done:
add rsp, 56
pop r12
pop rsi
pop rdi
pop rbx
ret
mem_aligned_realloc ENDP
END
+148
View File
@@ -0,0 +1,148 @@
; ============================================================================
; profiling.asm - 性能分析工具 (x86-64 MASM)
; 使用 RDTSC/RDTSCP 进行高精度计时
; ============================================================================
include asm_macros.inc
.DATA
ALIGN 8
cpu_freq DQ 0 ; CPU 频率 (Hz)
cpu_freq_init DB 0 ; 是否已初始化
.CODE
; ============================================================================
; RDTSC 计时
; ============================================================================
; uint64_t prof_rdtsc(void) -> RAX
; 返回: TSC (Time Stamp Counter) 值
prof_rdtsc PROC
; 使用 RDTSCP 获取有序的 TSC
rdtscp
; EDX:EAX = TSC
shl rdx, 32
or rax, rdx
ret
prof_rdtsc ENDP
; uint64_t prof_get_freq(void) -> RAX
; 获取 CPU 频率 (Hz), 使用 QPC 校准 TSC
prof_get_freq PROC
; 检查是否已初始化
cmp byte ptr [cpu_freq_init], 0
jnz prof_freq_return
push rbx
push rdi
push rsi
sub rsp, 40
; 获取 QPC 频率
lea rcx, [rsp + 32]
call QueryPerformanceFrequency
; 获取起始 QPC 和 TSC
lea rcx, [rsp + 24]
call QueryPerformanceCounter
rdtscp
shl rdx, 32
or rax, rdx
mov rbx, rax ; rbx = 起始 TSC
; 等待约 10ms
mov rcx, 10 ; 10 毫秒
call Sleep
; 获取结束 QPC 和 TSC
lea rcx, [rsp + 16]
call QueryPerformanceCounter
rdtscp
shl rdx, 32
or rax, rdx
mov rdi, rax ; rdi = 结束 TSC
; qpc_diff = end_qpc - start_qpc
mov rax, [rsp + 16]
sub rax, [rsp + 24]
mov rsi, rax ; rsi = qpc_diff
; tsc_diff = end_tsc - start_tsc
mov rcx, rdi
sub rcx, rbx ; rcx = tsc_diff
; cpu_freq = tsc_diff * qpc_freq / qpc_diff
mov rax, rcx ; rax = tsc_diff
mov rdx, [rsp + 32] ; rdx = qpc_freq
mul rdx ; rdx:rax = tsc_diff * qpc_freq
div rsi ; rax = freq (Hz)
mov [cpu_freq], rax
mov byte ptr [cpu_freq_init], 1
add rsp, 40
pop rsi
pop rdi
pop rbx
prof_freq_return:
mov rax, [cpu_freq]
ret
prof_get_freq ENDP
; double prof_cycles_to_ms(uint64_t cycles) -> XMM0
; 将 TSC cycles 转换为毫秒: ms = cycles * 1000 / freq
prof_cycles_to_ms PROC
mov rdx, rcx ; 保存 cycles
call prof_get_freq ; rax = CPU freq (Hz)
cvtsi2sd xmm0, rdx ; xmm0 = cycles (double)
mov rcx, 1000
cvtsi2sd xmm1, rcx ; xmm1 = 1000.0
mulsd xmm0, xmm1 ; cycles * 1000
cvtsi2sd xmm1, rax ; xmm1 = freq
divsd xmm0, xmm1 ; ms
ret
prof_cycles_to_ms ENDP
; ============================================================================
; 内联性能宏 (在 C 代码中使用)
; ============================================================================
; 获取当前时间戳 (直接 RDTSC, 无函数调用开销)
; 在 C 中使用: __rdtsc() 内联函数
; 高精度计时器结构
; struct ProfTimer {
; uint64_t start;
; uint64_t end;
; double elapsed_ms;
; };
; void prof_timer_start(ProfTimer* timer)
prof_timer_start PROC
rdtscp
shl rdx, 32
or rax, rdx
mov [rcx], rax ; timer->start = tsc
ret
prof_timer_start ENDP
; void prof_timer_stop(ProfTimer* timer)
prof_timer_stop PROC
rdtscp
shl rdx, 32
or rax, rdx
mov [rcx + 8], rax ; timer->end = tsc
; 计算 elapsed_ms
mov rax, [rcx + 8]
sub rax, [rcx] ; cycles
; 转换为 ms
call prof_cycles_to_ms
movsd qword ptr [rcx + 16], xmm0 ; timer->elapsed_ms
ret
prof_timer_stop ENDP
END
+304
View File
@@ -0,0 +1,304 @@
; ============================================================================
; string_ops.asm - 快速字符串/内存操作 (x86-64 MASM)
; 使用 SSE4.1 加速 memcpy/memset/strlen
; ============================================================================
include asm_macros.inc
.CODE
; ============================================================================
; 内存操作
; ============================================================================
; void fast_memcpy(void* dst, const void* src, uint64_t count)
; RCX = dst, RDX = src, R8 = count
fast_memcpy PROC
mov rax, rcx ; rax = dst
mov r9, r8 ; r9 = count
; 小于 16 字节: 逐字节复制
cmp r9, 16
jb memcpy_small
; 16-32 字节: 两次 128-bit 加载
cmp r9, 32
jb memcpy_16_32
; 32-64 字节: 四次 128-bit 加载
cmp r9, 64
jb memcpy_32_64
; >= 64 字节: SSE 批量复制
; 确保 16 字节对齐
mov r10, rcx
and r10, 0Fh ; 检查 dst 对齐
jnz memcpy_unaligned
memcpy_aligned_loop:
cmp r9, 64
jb memcpy_tail
; 每次复制 64 字节 (4x128-bit)
movaps xmm0, xmmword ptr [rdx]
movaps xmm1, xmmword ptr [rdx + 16]
movaps xmm2, xmmword ptr [rdx + 32]
movaps xmm3, xmmword ptr [rdx + 48]
movaps xmmword ptr [rcx], xmm0
movaps xmmword ptr [rcx + 16], xmm1
movaps xmmword ptr [rcx + 32], xmm2
movaps xmmword ptr [rcx + 48], xmm3
add rcx, 64
add rdx, 64
sub r9, 64
jmp memcpy_aligned_loop
memcpy_tail:
; 处理剩余 0-63 字节
test r9, 32
jz memcpy_tail_16
movaps xmm0, xmmword ptr [rdx]
movaps xmm1, xmmword ptr [rdx + 16]
movaps xmmword ptr [rcx], xmm0
movaps xmmword ptr [rcx + 16], xmm1
add rcx, 32
add rdx, 32
sub r9, 32
memcpy_tail_16:
test r9, 16
jz memcpy_tail_8
movaps xmm0, xmmword ptr [rdx]
movaps xmmword ptr [rcx], xmm0
memcpy_tail_8:
test r9, 8
jz memcpy_tail_4
mov r10, qword ptr [rdx]
mov qword ptr [rcx], r10
memcpy_tail_4:
test r9, 4
jz memcpy_tail_2
mov r10d, dword ptr [rdx]
mov dword ptr [rcx], r10d
memcpy_tail_2:
test r9, 2
jz memcpy_tail_1
mov r10w, word ptr [rdx]
mov word ptr [rcx], r10w
memcpy_tail_1:
test r9, 1
jz memcpy_done
mov r10b, byte ptr [rdx]
mov byte ptr [rcx], r10b
jmp memcpy_done
memcpy_unaligned:
; 非对齐时使用 movups
movups xmm0, xmmword ptr [rdx]
movups xmmword ptr [rcx], xmm0
add rcx, 16
add rdx, 16
sub r9, 16
jmp memcpy_aligned_loop
memcpy_32_64:
mov r10, qword ptr [rdx]
mov qword ptr [rcx], r10
mov r10, qword ptr [rdx + 8]
mov qword ptr [rcx + 8], r10
mov r10, qword ptr [rdx + 16]
mov qword ptr [rcx + 16], r10
mov r10, qword ptr [rdx + 24]
mov qword ptr [rcx + 24], r10
sub r9, 32
add rdx, 32
add rcx, 32
jmp memcpy_small
memcpy_16_32:
mov r10, qword ptr [rdx]
mov qword ptr [rcx], r10
mov r10, qword ptr [rdx + 8]
mov qword ptr [rcx + 8], r10
test r9, 8
jz memcpy_16_32_done
mov r10, qword ptr [rdx + 16]
mov qword ptr [rcx + 16], r10
memcpy_16_32_done:
jmp memcpy_done
memcpy_small:
test r9, r9
jz memcpy_done
mov r10, r9
and r10, 7 ; r10 = count % 8
shr r9, 3 ; r9 = count / 8
; 复制完整 8 字节
memcpy_small_loop:
test r9, r9
jz memcpy_small_tail
mov r11, qword ptr [rdx]
mov qword ptr [rcx], r11
add rdx, 8
add rcx, 8
dec r9
jmp memcpy_small_loop
memcpy_small_tail:
; 复制剩余字节
test r10, r10
jz memcpy_done
mov r11b, byte ptr [rdx]
mov byte ptr [rcx], r11b
inc rdx
inc rcx
dec r10
jmp memcpy_small_tail
memcpy_done:
mov rax, rax ; 返回 dst
ret
fast_memcpy ENDP
; void fast_memset(void* dst, int value, uint64_t count)
; RCX = dst, EDX = value, R8 = count
fast_memset PROC
mov rax, rcx
mov r9, r8
; 将 value 扩展到所有字节
movsx rdx, dl ; 符号扩展
mov rcx, rdx
shl rcx, 8
or rdx, rcx
mov rcx, rdx
shl rcx, 16
or rdx, rcx ; rdx = value 重复4次
mov rcx, rdx
shl rcx, 32
or rdx, rcx ; rdx = value 重复8次
; 复制到 XMM 寄存器
vmovq xmm0, rdx
vinsertf128 ymm0, ymm0, xmm0, 1 ; 256-bit 广播
; >= 32 字节: AVX 批量
cmp r9, 32
jb memset_small
memset_avx_loop:
cmp r9, 32
jb memset_tail
vmovdqu ymmword ptr [rax], ymm0
add rax, 32
sub r9, 32
jmp memset_avx_loop
memset_tail:
; 处理剩余 0-31 字节
test r9, 16
jz memset_tail_8
movups xmmword ptr [rax], xmm0
add rax, 16
sub r9, 16
memset_tail_8:
test r9, 8
jz memset_tail_4
mov qword ptr [rax], rdx
add rax, 8
memset_tail_4:
test r9, 4
jz memset_tail_2
mov dword ptr [rax], edx
add rax, 4
memset_tail_2:
test r9, 2
jz memset_tail_1
mov word ptr [rax], dx
add rax, 2
memset_tail_1:
test r9, 1
jz memset_done
mov byte ptr [rax], dl
jmp memset_done
memset_small:
test r9, r9
jz memset_done
mov byte ptr [rax], dl
inc rax
dec r9
jmp memset_small
memset_done:
vzeroupper
ret
fast_memset ENDP
; ============================================================================
; 字符串操作
; ============================================================================
; uint64_t fast_strlen(const char* str) -> RAX
; 使用 SSE4.2 PCMPESTRI 搜索 null terminator
fast_strlen PROC
mov rdx, rcx ; rdx = original str
pxor xmm0, xmm0 ; 搜索 0
strlen_sse_loop:
mov rax, rcx ; rax = 当前 16 字节块起始
movdqu xmm1, xmmword ptr [rcx]
pcmpestri xmm0, xmm1, 00001000b ; 搜索 0, CF=1 表示找到, ECX=偏移
jc strlen_found
add rcx, 16
jmp strlen_sse_loop
strlen_found:
; rax = 块起始, rcx (ecx) = 块内偏移
add rax, rcx
sub rax, rdx
ret
fast_strlen ENDP
; int fast_strcmp(const char* a, const char* b) -> EAX
; 返回: 0=相等, <0=a<b, >0=a>b
fast_strcmp PROC
xor rax, rax
strcmp_loop:
movzx r8d, byte ptr [rcx + rax]
movzx r9d, byte ptr [rdx + rax]
cmp r8b, r9b
jne strcmp_diff
test r8b, r8b
jz strcmp_equal
inc rax
jmp strcmp_loop
strcmp_diff:
mov eax, r8d
sub eax, r9d
ret
strcmp_equal:
xor eax, eax
ret
fast_strcmp ENDP
END
+30
View File
@@ -0,0 +1,30 @@
#pragma once
// ============================================================================
// mesh.h - 网格 (顶点缓冲 + 索引缓冲)
// ============================================================================
#include <d3d11.h>
#include <stdint.h>
typedef struct {
ID3D11Buffer* vertex_buffer;
ID3D11Buffer* index_buffer;
uint32_t vertex_count;
uint32_t index_count;
uint32_t vertex_stride;
} MeshBuffer;
// 从内存数据创建网格
MeshBuffer* mesh_create(ID3D11Device* device,
const void* vertices, uint32_t vertex_count, uint32_t vertex_stride,
const uint32_t* indices, uint32_t index_count);
// 从 OBJ 文件加载网格 (简化版: 仅顶点+面)
MeshBuffer* mesh_load_obj(ID3D11Device* device, const char* filepath);
// 销毁
void mesh_destroy(MeshBuffer* mesh);
// 绘制
void mesh_draw(ID3D11DeviceContext* ctx, MeshBuffer* mesh);
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <d3d11.h>
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
uint32_t width, height;
} Texture;
Texture* texture_load_bmp(ID3D11Device* dev, const char* path);
Texture* texture_create_white(ID3D11Device* dev, uint32_t w, uint32_t h);
void texture_destroy(Texture* tex);
+277
View File
@@ -0,0 +1,277 @@
#include "audio.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "xaudio2.lib")
// MinGW 没有 xaudio2.lib,通过系统 DLL 动态加载 XAudio2Create
typedef HRESULT (WINAPI *XAudio2CreateFunc)(IXAudio2** ppxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor);
static HRESULT engine_xaudio2_create(IXAudio2** ppxaudio2, UINT32 flags, XAUDIO2_PROCESSOR processor) {
static HMODULE mod = NULL;
static XAudio2CreateFunc func = NULL;
if (!func) {
mod = LoadLibraryA("xaudio2_9.dll");
if (!mod) mod = LoadLibraryA("xaudio2_8.dll");
if (!mod) mod = LoadLibraryA("xaudio2_7.dll");
if (mod) func = (XAudio2CreateFunc)GetProcAddress(mod, "XAudio2Create");
}
if (!func) return E_FAIL;
return func(ppxaudio2, flags, processor);
}
typedef struct {
WAVEFORMATEX format;
uint8_t* data;
uint32_t size;
} WavData;
static int wav_load(const uint8_t* file_data, uint32_t file_size, WavData* out) {
memset(out, 0, sizeof(*out));
if (file_size < 12) return 0;
if (memcmp(file_data, "RIFF", 4) != 0) return 0;
if (memcmp(file_data + 8, "WAVE", 4) != 0) return 0;
uint32_t offset = 12;
int has_fmt = 0;
int has_data = 0;
while (offset + 8 <= file_size) {
const uint8_t* chunk_id = file_data + offset;
uint32_t chunk_size = *(const uint32_t*)(file_data + offset + 4);
const uint8_t* chunk_data = file_data + offset + 8;
if (offset + 8 + chunk_size > file_size) return 0;
if (memcmp(chunk_id, "fmt ", 4) == 0 && chunk_size >= 16) {
uint32_t copy_size = chunk_size >= sizeof(WAVEFORMATEX) ? sizeof(WAVEFORMATEX) : chunk_size;
memcpy(&out->format, chunk_data, copy_size);
if (out->format.wFormatTag != WAVE_FORMAT_PCM && out->format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT) {
return 0;
}
if (out->format.cbSize != 0) {
out->format.cbSize = 0;
}
has_fmt = 1;
} else if (memcmp(chunk_id, "data", 4) == 0) {
out->data = (uint8_t*)chunk_data;
out->size = chunk_size;
has_data = 1;
}
offset += 8 + chunk_size;
if (chunk_size & 1) offset += 1; // 对齐到偶数字节
}
return has_fmt && has_data;
}
AudioSystem* audio_create(void) {
CoInitializeEx(NULL, COINIT_MULTITHREADED);
AudioSystem* audio = (AudioSystem*)calloc(1, sizeof(AudioSystem));
if (!audio) return NULL;
HRESULT hr = engine_xaudio2_create(&audio->xaudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (FAILED(hr) || !audio->xaudio2) {
free(audio);
return NULL;
}
hr = audio->xaudio2->lpVtbl->CreateMasteringVoice(
audio->xaudio2,
&audio->mastering_voice,
XAUDIO2_DEFAULT_CHANNELS,
XAUDIO2_DEFAULT_SAMPLERATE,
0,
NULL,
NULL,
AudioCategory_GameEffects);
if (FAILED(hr)) {
audio->xaudio2->lpVtbl->Release(audio->xaudio2);
free(audio);
return NULL;
}
return audio;
}
void audio_destroy(AudioSystem* audio) {
if (!audio) return;
if (audio->mastering_voice) {
audio->mastering_voice->lpVtbl->DestroyVoice(audio->mastering_voice);
}
if (audio->xaudio2) {
audio->xaudio2->lpVtbl->Release(audio->xaudio2);
}
free(audio);
CoUninitialize();
}
AudioSource* audio_source_create(AudioSystem* audio) {
if (!audio) return NULL;
AudioSource* source = (AudioSource*)calloc(1, sizeof(AudioSource));
if (!source) return NULL;
source->system = audio;
source->volume = 1.0f;
source->pitch = 1.0f;
source->loop = 0;
source->state = AUDIO_STOPPED;
return source;
}
void audio_source_destroy(AudioSource* source) {
if (!source) return;
if (source->source) {
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->DestroyVoice(source->source);
}
free(source->audio_data);
free(source);
}
static int audio_source_load_data(AudioSource* source, const WavData* wav) {
if (!source || !source->system || !source->system->xaudio2) return 0;
// 释放旧的语音与数据
if (source->source) {
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->DestroyVoice(source->source);
source->source = NULL;
}
free(source->audio_data);
source->audio_data = NULL;
source->audio_size = 0;
source->audio_data = (uint8_t*)malloc(wav->size);
if (!source->audio_data) return 0;
memcpy(source->audio_data, wav->data, wav->size);
source->audio_size = wav->size;
HRESULT hr = source->system->xaudio2->lpVtbl->CreateSourceVoice(
source->system->xaudio2,
&source->source,
&wav->format,
0,
XAUDIO2_DEFAULT_FREQ_RATIO,
NULL,
NULL,
NULL);
if (FAILED(hr)) {
free(source->audio_data);
source->audio_data = NULL;
source->audio_size = 0;
return 0;
}
return 1;
}
int audio_load_wav(AudioSource* source, const char* path) {
if (!source || !path) return 0;
FILE* f = fopen(path, "rb");
if (!f) return 0;
fseek(f, 0, SEEK_END);
long file_size = ftell(f);
fseek(f, 0, SEEK_SET);
if (file_size <= 0) {
fclose(f);
return 0;
}
uint8_t* file_data = (uint8_t*)malloc((size_t)file_size);
if (!file_data) {
fclose(f);
return 0;
}
size_t read = fread(file_data, 1, (size_t)file_size, f);
fclose(f);
if (read != (size_t)file_size) {
free(file_data);
return 0;
}
WavData wav;
int ok = wav_load(file_data, (uint32_t)file_size, &wav);
if (ok) {
ok = audio_source_load_data(source, &wav);
}
free(file_data);
return ok;
}
int audio_load_wav_memory(AudioSource* source, const uint8_t* data, uint32_t size) {
if (!source || !data || size == 0) return 0;
WavData wav;
int ok = wav_load(data, size, &wav);
if (ok) {
ok = audio_source_load_data(source, &wav);
}
return ok;
}
void audio_source_play(AudioSource* source) {
if (!source || !source->source) return;
XAUDIO2_BUFFER buffer = {0};
buffer.AudioBytes = source->audio_size;
buffer.pAudioData = source->audio_data;
buffer.Flags = XAUDIO2_END_OF_STREAM;
if (source->loop) {
buffer.LoopCount = XAUDIO2_LOOP_INFINITE;
}
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->FlushSourceBuffers(source->source);
source->source->lpVtbl->SubmitSourceBuffer(source->source, &buffer, NULL);
source->source->lpVtbl->Start(source->source, 0, XAUDIO2_COMMIT_NOW);
source->state = AUDIO_PLAYING;
}
void audio_source_stop(AudioSource* source) {
if (!source || !source->source) return;
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->source->lpVtbl->FlushSourceBuffers(source->source);
source->state = AUDIO_STOPPED;
}
void audio_source_pause(AudioSource* source) {
if (!source || !source->source) return;
source->source->lpVtbl->Stop(source->source, 0, XAUDIO2_COMMIT_NOW);
source->state = AUDIO_PAUSED;
}
void audio_source_resume(AudioSource* source) {
if (!source || !source->source) return;
source->source->lpVtbl->Start(source->source, 0, XAUDIO2_COMMIT_NOW);
source->state = AUDIO_PLAYING;
}
void audio_source_set_volume(AudioSource* source, float volume) {
if (!source || !source->source) return;
source->volume = volume;
source->source->lpVtbl->SetVolume(source->source, volume, XAUDIO2_COMMIT_NOW);
}
void audio_source_set_pitch(AudioSource* source, float pitch) {
if (!source || !source->source) return;
source->pitch = pitch;
source->source->lpVtbl->SetFrequencyRatio(source->source, pitch, XAUDIO2_COMMIT_NOW);
}
void audio_source_set_loop(AudioSource* source, int loop) {
if (!source) return;
source->loop = loop;
}
void audio_set_master_volume(AudioSystem* audio, float volume) {
if (!audio || !audio->mastering_voice) return;
audio->mastering_voice->lpVtbl->SetVolume(audio->mastering_voice, volume, XAUDIO2_COMMIT_NOW);
}
+62
View File
@@ -0,0 +1,62 @@
#pragma once
// ============================================================================
// audio.h - 音频系统 (XAudio2)
// ============================================================================
#include <xaudio2.h>
#include <stdint.h>
// 播放状态
typedef enum {
AUDIO_STOPPED = 0,
AUDIO_PLAYING,
AUDIO_PAUSED
} AudioState;
struct AudioSystem;
// 音频源
typedef struct {
struct AudioSystem* system;
IXAudio2SourceVoice* source;
IXAudio2Voice* mastering;
float volume;
float pitch;
int loop;
AudioState state;
uint8_t* audio_data;
uint32_t audio_size;
} AudioSource;
// 音频管理器
typedef struct AudioSystem {
IXAudio2* xaudio2;
IXAudio2MasteringVoice* mastering_voice;
} AudioSystem;
// 初始化/销毁
AudioSystem* audio_create(void);
void audio_destroy(AudioSystem* audio);
// 音频源
AudioSource* audio_source_create(AudioSystem* audio);
void audio_source_destroy(AudioSource* source);
// 播放
void audio_source_play(AudioSource* source);
void audio_source_stop(AudioSource* source);
void audio_source_pause(AudioSource* source);
void audio_source_resume(AudioSource* source);
// 音量/音调
void audio_source_set_volume(AudioSource* source, float volume);
void audio_source_set_pitch(AudioSource* source, float pitch);
void audio_source_set_loop(AudioSource* source, int loop);
// WAV加载
int audio_load_wav(AudioSource* source, const char* path);
int audio_load_wav_memory(AudioSource* source, const uint8_t* data, uint32_t size);
// 主音量
void audio_set_master_volume(AudioSystem* audio, float volume);
+192
View File
@@ -0,0 +1,192 @@
#include "asset_manager.h"
#include "../Rendering/mesh.h"
#include "../Rendering/texture.h"
#include "../Rendering/shader.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define ASSET_BUCKET_COUNT 256
struct AssetManager {
Asset* buckets[ASSET_BUCKET_COUNT];
uint32_t next_id;
};
static uint32_t hash_str(const char* s) {
uint32_t h = 5381;
while (*s) h = ((h << 5) + h) + (unsigned char)*s++;
return h % ASSET_BUCKET_COUNT;
}
static int str_ends_with(const char* s, const char* suffix) {
size_t ls = strlen(s), lf = strlen(suffix);
return ls >= lf && strcmp(s + ls - lf, suffix) == 0;
}
static Asset* asset_alloc(AssetManager* am, AssetType type, const char* path, void* data, void (*destroy)(void*)) {
Asset* a = (Asset*)calloc(1, sizeof(Asset));
a->id = ++am->next_id;
a->type = type;
a->refcount = 1;
a->owner = am;
strncpy(a->path, path, sizeof(a->path) - 1);
a->data = data;
a->destroy = destroy;
uint32_t h = hash_str(path);
a->next = am->buckets[h];
am->buckets[h] = a;
return a;
}
static void asset_free(AssetManager* am, Asset* a) {
if (!a) return;
uint32_t h = hash_str(a->path);
Asset** cur = &am->buckets[h];
while (*cur) {
if (*cur == a) {
*cur = a->next;
break;
}
cur = &(*cur)->next;
}
if (a->destroy) a->destroy(a->data);
free(a);
}
AssetManager* asset_manager_create(void) {
AssetManager* am = (AssetManager*)calloc(1, sizeof(AssetManager));
return am;
}
void asset_manager_destroy(AssetManager* am) {
if (!am) return;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
Asset* a = am->buckets[i];
while (a) {
Asset* next = a->next;
if (a->destroy) a->destroy(a->data);
free(a);
a = next;
}
}
free(am);
}
Asset* asset_manager_find(AssetManager* am, const char* path) {
if (!am || !path) return NULL;
uint32_t h = hash_str(path);
for (Asset* a = am->buckets[h]; a; a = a->next) {
if (strcmp(a->path, path) == 0) return a;
}
return NULL;
}
Asset* asset_manager_get(AssetManager* am, uint32_t id) {
if (!am || id == 0) return NULL;
for (int i = 0; i < ASSET_BUCKET_COUNT; i++) {
for (Asset* a = am->buckets[i]; a; a = a->next) {
if (a->id == id) return a;
}
}
return NULL;
}
void* asset_data(Asset* a) { return a ? a->data : NULL; }
void asset_acquire(Asset* a) { if (a) a->refcount++; }
void asset_release(Asset* a) {
if (!a) return;
a->refcount--;
if (a->refcount <= 0 && a->owner) {
asset_free(a->owner, a);
}
}
// ------------------------------------------------------------------
// Mesh
// ------------------------------------------------------------------
static void destroy_mesh_asset(void* data) { mesh_destroy((Mesh*)data); }
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Mesh* mesh = NULL;
if (strcmp(path, "cube") == 0) mesh = mesh_create_cube(dev);
else if (strcmp(path, "sphere") == 0) mesh = mesh_create_sphere(dev, 1.0f, 16);
else if (strcmp(path, "plane") == 0) mesh = mesh_create_plane(dev, 10.0f);
else if (str_ends_with(path, ".obj")) mesh = mesh_load_obj(dev, path);
if (!mesh) return NULL;
return asset_alloc(am, ASSET_MESH, path, mesh, destroy_mesh_asset);
}
// ------------------------------------------------------------------
// Texture
// ------------------------------------------------------------------
static void destroy_texture_asset(void* data) { texture_destroy((Texture*)data); }
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path) {
if (!am || !dev || !path) return NULL;
Asset* existing = asset_manager_find(am, path);
if (existing) { asset_acquire(existing); return existing; }
Texture* tex = NULL;
if (strcmp(path, "checkerboard") == 0) tex = texture_create_checkerboard(dev, 256, 8);
else tex = texture_load_from_file(dev, path);
if (!tex) return NULL;
return asset_alloc(am, ASSET_TEXTURE, path, tex, destroy_texture_asset);
}
// ------------------------------------------------------------------
// Shader
// ------------------------------------------------------------------
static void destroy_shader_asset(void* data) { shader_destroy((Shader*)data); }
static char* read_file(const char* path, uint32_t* out_len) {
FILE* f = fopen(path, "rb");
if (!f) return NULL;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
if (size <= 0) { fclose(f); return NULL; }
char* buf = (char*)malloc((size_t)size + 1);
if (!buf) { fclose(f); return NULL; }
fread(buf, 1, (size_t)size, f);
buf[size] = '\0';
fclose(f);
if (out_len) *out_len = (uint32_t)size;
return buf;
}
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path) {
if (!am || !dev) return NULL;
char key[512];
if (vs_path && ps_path) snprintf(key, sizeof(key), "%s|%s", vs_path, ps_path);
else strcpy(key, "default");
Asset* existing = asset_manager_find(am, key);
if (existing) { asset_acquire(existing); return existing; }
Shader* s = shader_create();
if (vs_path && ps_path) {
uint32_t vs_len = 0, ps_len = 0;
char* vs = read_file(vs_path, &vs_len);
char* ps = read_file(ps_path, &ps_len);
if (!vs || !ps || !shader_compile(s, dev, vs, ps, vs_len, ps_len)) {
shader_destroy(s); free(vs); free(ps); return NULL;
}
free(vs); free(ps);
} else {
if (!shader_compile(s, dev, NULL, NULL, 0, 0)) {
shader_destroy(s); return NULL;
}
}
return asset_alloc(am, ASSET_SHADER, key, s, destroy_shader_asset);
}
+48
View File
@@ -0,0 +1,48 @@
#pragma once
#include <stdint.h>
#include <d3d11.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct AssetManager AssetManager;
typedef struct Asset Asset;
typedef enum {
ASSET_MESH = 0,
ASSET_TEXTURE,
ASSET_SHADER,
ASSET_MATERIAL,
ASSET_COUNT
} AssetType;
struct Asset {
uint32_t id;
AssetType type;
int refcount;
char path[256];
void* data;
void (*destroy)(void* data);
Asset* next;
struct AssetManager* owner;
};
AssetManager* asset_manager_create(void);
void asset_manager_destroy(AssetManager* am);
Asset* asset_manager_load_mesh(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_texture(AssetManager* am, ID3D11Device* dev, const char* path);
Asset* asset_manager_load_shader(AssetManager* am, ID3D11Device* dev, const char* vs_path, const char* ps_path);
Asset* asset_manager_find(AssetManager* am, const char* path);
Asset* asset_manager_get(AssetManager* am, uint32_t id);
void* asset_data(Asset* a);
void asset_acquire(Asset* a);
void asset_release(Asset* a);
#ifdef __cplusplus
}
#endif
+244
View File
@@ -0,0 +1,244 @@
// ============================================================================
// engine.c - 引擎核心实现
// ============================================================================
#include "engine.h"
#include "logger.h"
#include "timer.h"
#include "../Window/window.h"
#include "../Rendering/renderer.h"
#include "../Rendering/graphics_device.h"
#include "../include/engine_api.h"
#include <stdlib.h>
#include <Windows.h>
// 引擎实例 (单例)
static Engine g_engine = {0};
// 回调
static EngineUpdateCallback g_update_callback = NULL;
static EngineRenderCallback g_render_callback = NULL;
static EngineKeyCallback g_key_callback = NULL;
static EngineResizeCallback g_resize_callback = NULL;
static EngineMouseMoveCallback g_mouse_move_callback = NULL;
static EngineMouseButtonCallback g_mouse_button_callback = NULL;
// 内部函数
static void on_key(int key) {
if (g_key_callback) {
g_key_callback(&g_engine, key);
}
if (key == VK_ESCAPE) {
engine_request_quit(&g_engine);
}
}
static void on_resize(int width, int height) {
if (g_resize_callback) {
g_resize_callback(&g_engine, width, height);
}
}
static void on_mouse_move(int x, int y) {
if (g_mouse_move_callback) {
g_mouse_move_callback(&g_engine, x, y);
}
}
static void on_mouse_button(int button, int pressed) {
if (g_mouse_button_callback) {
g_mouse_button_callback(&g_engine, button, pressed);
}
}
Engine* engine_create(void) {
return &g_engine;
}
void engine_destroy(Engine* engine) {
if (engine) {
engine_shutdown(engine);
}
}
int engine_init(Engine* engine, const EngineConfig* config) {
if (engine->initialized) return 1;
engine->config = *config;
// 初始化日志
logger_init("engine.log");
LOG_INFO("Initializing Lity3D Engine...");
// 初始化计时器
timer_init();
// 创建窗口
engine->window = window_create(
config->app_name,
config->window_width,
config->window_height,
config->fullscreen
);
if (!engine->window) {
LOG_ERROR("Failed to create window!");
return 0;
}
// 设置回调
window_set_key_callback(engine->window, on_key);
window_set_resize_callback(engine->window, on_resize);
window_set_mouse_move_callback(engine->window, on_mouse_move);
window_set_mouse_button_callback(engine->window, on_mouse_button);
LOG_INFO("Window created: %dx%d", config->window_width, config->window_height);
// 创建 D3D11 图形设备
engine->graphics_device = graphics_device_create();
if (!graphics_device_init(engine->graphics_device,
window_get_handle(engine->window),
config->window_width,
config->window_height,
config->vsync)) {
LOG_ERROR("Failed to initialize D3D11 graphics device!");
window_destroy(engine->window);
engine->window = NULL;
return 0;
}
// 创建渲染器
engine->renderer = renderer_create(engine->graphics_device);
if (!engine->renderer) {
LOG_ERROR("Failed to create renderer!");
graphics_device_destroy(engine->graphics_device);
engine->graphics_device = NULL;
window_destroy(engine->window);
engine->window = NULL;
return 0;
}
LOG_INFO("Graphics device and renderer ready.");
engine->initialized = 1;
engine->running = 1;
engine->frame_count = 0;
LOG_INFO("Lity3D Engine initialized successfully.");
return 1;
}
void engine_run(Engine* engine) {
if (!engine->initialized) {
LOG_ERROR("Engine not initialized!");
return;
}
double fixed_dt = 1.0 / 60.0; // 60Hz 固定时间步
double accumulator = 0.0;
double fps_timer = 0.0;
int fps_frames = 0;
int fps_display = 0;
while (engine->running) {
// 处理窗口消息
if (!window_process_messages(engine->window)) {
engine->running = 0;
break;
}
// 更新计时
timer_tick();
engine->delta_time = timer_get_delta();
engine->total_time += engine->delta_time;
engine->frame_count++;
// FPS 计算
fps_timer += engine->delta_time;
fps_frames++;
if (fps_timer >= 1.0) {
fps_display = fps_frames;
fps_frames = 0;
fps_timer -= 1.0;
}
// 固定时间步逻辑更新
accumulator += engine->delta_time;
while (accumulator >= fixed_dt) {
if (g_update_callback) {
g_update_callback(engine, fixed_dt);
}
accumulator -= fixed_dt;
}
// 渲染
if (g_render_callback) {
g_render_callback(engine);
}
// 存储 FPS 供外部读取
engine->fps = fps_display;
}
}
void engine_shutdown(Engine* engine) {
if (!engine->initialized) return;
LOG_INFO("Shutting down Lity3D Engine...");
if (engine->renderer) {
renderer_destroy(engine->renderer);
engine->renderer = NULL;
}
if (engine->graphics_device) {
graphics_device_destroy(engine->graphics_device);
engine->graphics_device = NULL;
}
if (engine->window) {
window_destroy(engine->window);
engine->window = NULL;
}
logger_shutdown();
engine->initialized = 0;
engine->running = 0;
}
void engine_request_quit(Engine* engine) {
engine->running = 0;
}
// 回调设置
void engine_set_update_callback(Engine* engine, EngineUpdateCallback callback) {
g_update_callback = callback;
}
void engine_set_render_callback(Engine* engine, EngineRenderCallback callback) {
g_render_callback = callback;
}
void engine_set_key_callback(Engine* engine, EngineKeyCallback callback) {
g_key_callback = callback;
}
void engine_set_resize_callback(Engine* engine, EngineResizeCallback callback) {
g_resize_callback = callback;
}
void engine_set_mouse_move_callback(Engine* engine, EngineMouseMoveCallback callback) {
g_mouse_move_callback = callback;
}
void engine_set_mouse_button_callback(Engine* engine, EngineMouseButtonCallback callback) {
g_mouse_button_callback = callback;
}
GraphicsDevice* engine_get_graphics_device(Engine* engine) {
return engine ? engine->graphics_device : NULL;
}
Renderer* engine_get_renderer(Engine* engine) {
return engine ? (Renderer*)engine->renderer : NULL;
}
+73
View File
@@ -0,0 +1,73 @@
#pragma once
// ============================================================================
// engine.h - 引擎核心
// ============================================================================
#include <stdint.h>
// 前向声明
typedef struct Window Window;
typedef struct GraphicsDevice GraphicsDevice;
typedef struct Renderer Renderer;
// 引擎配置
typedef struct {
const char* app_name;
int window_width;
int window_height;
int fullscreen;
int vsync;
} EngineConfig;
// 引擎状态
typedef struct {
int running;
int initialized;
EngineConfig config;
Window* window;
void* renderer;
GraphicsDevice* graphics_device;
double delta_time;
double total_time;
int fps;
uint64_t frame_count;
} Engine;
// 初始化默认配置
static inline EngineConfig engine_default_config(void) {
EngineConfig config;
config.app_name = "Lity3D Engine";
config.window_width = 1280;
config.window_height = 720;
config.fullscreen = 0;
config.vsync = 1;
return config;
}
// 引擎函数
Engine* engine_create(void);
void engine_destroy(Engine* engine);
int engine_init(Engine* engine, const EngineConfig* config);
void engine_run(Engine* engine);
void engine_shutdown(Engine* engine);
void engine_request_quit(Engine* engine);
// 回调
typedef void (*EngineUpdateCallback)(Engine* engine, double delta_time);
typedef void (*EngineRenderCallback)(Engine* engine);
typedef void (*EngineKeyCallback)(Engine* engine, int key);
typedef void (*EngineResizeCallback)(Engine* engine, int width, int height);
typedef void (*EngineMouseMoveCallback)(Engine* engine, int x, int y);
typedef void (*EngineMouseButtonCallback)(Engine* engine, int button, int pressed);
void engine_set_update_callback(Engine* engine, EngineUpdateCallback callback);
void engine_set_render_callback(Engine* engine, EngineRenderCallback callback);
void engine_set_key_callback(Engine* engine, EngineKeyCallback callback);
void engine_set_resize_callback(Engine* engine, EngineResizeCallback callback);
void engine_set_mouse_move_callback(Engine* engine, EngineMouseMoveCallback callback);
void engine_set_mouse_button_callback(Engine* engine, EngineMouseButtonCallback callback);
// 子系统访问
GraphicsDevice* engine_get_graphics_device(Engine* engine);
Renderer* engine_get_renderer(Engine* engine);
+73
View File
@@ -0,0 +1,73 @@
// ============================================================================
// logger.c - 日志系统实现
// ============================================================================
#include "logger.h"
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
static FILE* g_log_file = NULL;
static LogLevel g_log_level = LOG_TRACE;
static const char* level_strings[] = {
"TRACE", "DEBUG", "INFO ", "WARN ", "ERROR", "FATAL"
};
void logger_init(const char* log_file) {
if (log_file) {
g_log_file = fopen(log_file, "w");
if (g_log_file) {
fprintf(g_log_file, "=== Lity3D Engine Log ===\n");
fflush(g_log_file);
}
}
printf("=== Lity3D Engine Log ===\n");
}
void logger_shutdown(void) {
if (g_log_file) {
fclose(g_log_file);
g_log_file = NULL;
}
}
void logger_set_level(LogLevel level) {
g_log_level = level;
}
void logger_log(LogLevel level, const char* file, int line, const char* fmt, ...) {
if (level < g_log_level) return;
// 时间戳
time_t now = time(NULL);
struct tm* tm_info = localtime(&now);
char time_str[20];
strftime(time_str, sizeof(time_str), "%H:%M:%S", tm_info);
// 文件名 (去掉路径)
const char* filename = strrchr(file, '\\');
if (!filename) filename = strrchr(file, '/');
if (filename) filename++; else filename = file;
// 格式化消息
char msg[2048];
va_list args;
va_start(args, fmt);
vsnprintf(msg, sizeof(msg), fmt, args);
va_end(args);
// 输出到控制台
if (level >= LOG_WARN) {
fprintf(stderr, "[%s] [%s] %s:%d - %s\n", time_str, level_strings[level], filename, line, msg);
} else {
printf("[%s] [%s] %s:%d - %s\n", time_str, level_strings[level], filename, line, msg);
}
// 输出到文件
if (g_log_file) {
fprintf(g_log_file, "[%s] [%s] %s:%d - %s\n", time_str, level_strings[level], filename, line, msg);
fflush(g_log_file);
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
// ============================================================================
// logger.h - 日志系统
// ============================================================================
#include <stdio.h>
// 日志级别
typedef enum {
LOG_TRACE = 0,
LOG_DEBUG,
LOG_INFO,
LOG_WARN,
LOG_ERROR,
LOG_FATAL
} LogLevel;
// 初始化/关闭
void logger_init(const char* log_file);
void logger_shutdown(void);
// 设置日志级别
void logger_set_level(LogLevel level);
// 日志函数
void logger_log(LogLevel level, const char* file, int line, const char* fmt, ...);
// 便捷宏
#define LOG_TRACE(...) logger_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_DEBUG(...) logger_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_INFO(...) logger_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_WARN(...) logger_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_ERROR(...) logger_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__)
#define LOG_FATAL(...) logger_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__)
+78
View File
@@ -0,0 +1,78 @@
// ============================================================================
// timer.c - 高精度计时器实现 (使用 rdtsc.asm)
// ============================================================================
#include "timer.h"
#include "../include/engine_api.h"
#include <Windows.h>
static double g_seconds_per_count = 0.0;
static int64_t g_prev_time = 0;
static int64_t g_curr_time = 0;
static int64_t g_start_time = 0;
static double g_delta_time = 0.0;
static double g_total_time = 0.0;
static double g_fps = 0.0;
static uint64_t g_frame_count = 0;
static double g_fps_accumulator = 0.0;
static uint64_t g_fps_frame_count = 0;
void timer_init(void) {
// 使用 QueryPerformanceCounter 获取高精度时间
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
g_seconds_per_count = 1.0 / (double)freq.QuadPart;
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
g_start_time = now.QuadPart;
g_prev_time = now.QuadPart;
g_curr_time = now.QuadPart;
g_delta_time = 0.0;
g_total_time = 0.0;
g_fps = 0.0;
g_frame_count = 0;
g_fps_accumulator = 0.0;
g_fps_frame_count = 0;
}
void timer_reset(void) {
timer_init();
}
void timer_tick(void) {
LARGE_INTEGER now;
QueryPerformanceCounter(&now);
g_curr_time = now.QuadPart;
g_delta_time = (g_curr_time - g_prev_time) * g_seconds_per_count;
g_total_time = (g_curr_time - g_start_time) * g_seconds_per_count;
g_prev_time = g_curr_time;
g_frame_count++;
g_fps_frame_count++;
g_fps_accumulator += g_delta_time;
if (g_fps_accumulator >= 1.0) {
g_fps = (double)g_fps_frame_count / g_fps_accumulator;
g_fps_frame_count = 0;
g_fps_accumulator = 0.0;
}
}
double timer_get_delta(void) {
return g_delta_time;
}
double timer_get_total(void) {
return g_total_time;
}
double timer_get_fps(void) {
return g_fps;
}
uint64_t timer_get_frame_count(void) {
return g_frame_count;
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
// ============================================================================
// timer.h - 高精度计时器
// ============================================================================
#include <stdint.h>
// 初始化计时器
void timer_init(void);
// 重置计时器
void timer_reset(void);
// 更新一帧
void timer_tick(void);
// 获取帧间隔 (秒)
double timer_get_delta(void);
// 获取总时间 (秒)
double timer_get_total(void);
// 获取 FPS
double timer_get_fps(void);
// 获取帧数
uint64_t timer_get_frame_count(void);
+100
View File
@@ -0,0 +1,100 @@
#include "world.h"
#include <stdlib.h>
#include <string.h>
World* world_create(void) {
World* w = (World*)calloc(1, sizeof(World));
w->next_entity = 1;
return w;
}
void world_destroy(World* world) {
if (!world) return;
for (uint32_t i = 0; i < world->entity_count; i++) {
Entity e = world->entities[i];
for (uint32_t c = 0; c < world->component_count; c++) {
if (world->components[c][e] && world->component_tables[c].destroy) {
world->component_tables[c].destroy(world->components[c][e]);
}
free(world->components[c][e]);
}
}
free(world);
}
Entity world_create_entity(World* world) {
if (world->entity_count >= MAX_ENTITIES) return 0;
Entity e = world->next_entity++;
world->entities[world->entity_count++] = e;
world->entity_masks[e] = 0;
return e;
}
void world_destroy_entity(World* world, Entity entity) {
for (uint32_t c = 0; c < world->component_count; c++) {
if (world->components[c][entity] && world->component_tables[c].destroy) {
world->component_tables[c].destroy(world->components[c][entity]);
}
free(world->components[c][entity]);
world->components[c][entity] = NULL;
}
world->entity_masks[entity] = 0;
for (uint32_t i = 0; i < world->entity_count; i++) {
if (world->entities[i] == entity) {
world->entities[i] = world->entities[--world->entity_count];
break;
}
}
}
int world_entity_alive(World* world, Entity entity) {
for (uint32_t i = 0; i < world->entity_count; i++) {
if (world->entities[i] == entity) return 1;
}
return 0;
}
ComponentType world_register_component(World* world, uint32_t size, ComponentVTable vtable) {
ComponentType type = world->component_count++;
world->component_sizes[type] = size;
world->component_tables[type] = vtable;
return type;
}
void* world_add_component(World* world, Entity entity, ComponentType type) {
if (world->components[type][entity]) return world->components[type][entity];
void* comp = calloc(1, world->component_sizes[type]);
world->components[type][entity] = comp;
world->entity_masks[entity] |= (1ULL << type);
return comp;
}
void* world_get_component(World* world, Entity entity, ComponentType type) {
return world->components[type][entity];
}
void world_remove_component(World* world, Entity entity, ComponentType type) {
if (world->components[type][entity]) {
if (world->component_tables[type].destroy) {
world->component_tables[type].destroy(world->components[type][entity]);
}
free(world->components[type][entity]);
world->components[type][entity] = NULL;
world->entity_masks[entity] &= ~(1ULL << type);
}
}
int world_has_component(World* world, Entity entity, ComponentType type) {
return (world->entity_masks[entity] & (1ULL << type)) != 0;
}
void world_each(World* world, uint64_t mask, SystemFunc func, void* data) {
for (uint32_t i = 0; i < world->entity_count; i++) {
Entity e = world->entities[i];
if ((world->entity_masks[e] & mask) == mask) {
func(world, e, data);
}
}
}
+53
View File
@@ -0,0 +1,53 @@
#pragma once
// ============================================================================
// world.h - 实体组件系统 (ECS)
// ============================================================================
#include <stdint.h>
#include "../include/engine_api.h"
#define MAX_ENTITIES 4096
#define MAX_COMPONENTS 32
typedef uint32_t Entity;
typedef uint32_t ComponentType;
// 组件接口
typedef struct {
void (*destroy)(void* component);
} ComponentVTable;
// 世界
typedef struct {
Entity entities[MAX_ENTITIES];
uint32_t entity_count;
uint32_t next_entity;
void* components[MAX_COMPONENTS][MAX_ENTITIES];
ComponentVTable component_tables[MAX_COMPONENTS];
uint32_t component_sizes[MAX_COMPONENTS];
uint32_t component_count;
uint64_t entity_masks[MAX_ENTITIES];
} World;
// 创建/销毁
World* world_create(void);
void world_destroy(World* world);
// 实体
Entity world_create_entity(World* world);
void world_destroy_entity(World* world, Entity entity);
int world_entity_alive(World* world, Entity entity);
// 组件
ComponentType world_register_component(World* world, uint32_t size, ComponentVTable vtable);
void* world_add_component(World* world, Entity entity, ComponentType type);
void* world_get_component(World* world, Entity entity, ComponentType type);
void world_remove_component(World* world, Entity entity, ComponentType type);
int world_has_component(World* world, Entity entity, ComponentType type);
// 系统遍历
typedef void (*SystemFunc)(World* world, Entity entity, void* data);
void world_each(World* world, uint64_t mask, SystemFunc func, void* data);
+303
View File
@@ -0,0 +1,303 @@
#include "bitmap_font.h"
#include <stdlib.h>
#include <string.h>
#include "../Core/logger.h"
static const uint8_t FONT_DATA[FONT_NUM_CHARS][FONT_CHAR_H] = {
{0},{0x00,0x00,0x18,0x3C,0x3C,0x3C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0x00,0x66,0x66,0x66,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x6C,0x6C,0xFE,0x6C,0x6C,0xFE,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00},
{0x18,0x18,0x7C,0xC6,0xC2,0xC0,0x7C,0x06,0x06,0x86,0xC6,0x7C,0x18,0x18,0x00,0x00},
{0x00,0x00,0x00,0x00,0xC2,0xC6,0x0C,0x18,0x30,0x60,0xC6,0x86,0x00,0x00,0x00,0x00},
{0x00,0x00,0x38,0x6C,0x6C,0x38,0x76,0xDC,0xCC,0xCC,0xCC,0x76,0x00,0x00,0x00,0x00},
{0x00,0x30,0x30,0x30,0x70,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x0C,0x18,0x30,0x30,0x30,0x30,0x30,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x30,0x18,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x18,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x7E,0x18,0x18,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x18,0x30,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x02,0x06,0x0C,0x18,0x30,0x60,0xC0,0x80,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xF6,0xE6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00},
{0},{0},{0},
{0x00,0x00,0x00,0x0C,0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
{0},{0},
{0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0},{0},{0},{0},{0},{0},{0},{0},{0},
{0x00,0x00,0x7C,0xC6,0xC6,0xCE,0xDE,0xF6,0xE6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x18,0x38,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x0C,0x18,0x30,0x60,0xC0,0xC6,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0x06,0x06,0x3C,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x0C,0x1C,0x3C,0x6C,0xCC,0xFE,0x0C,0x0C,0x0C,0x1E,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC0,0xC0,0xC0,0xFC,0x06,0x06,0x06,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x38,0x60,0xC0,0xC0,0xFC,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC6,0x06,0x06,0x0C,0x18,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7C,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0x7E,0x06,0x06,0x06,0x0C,0x78,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x30,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x0C,0x18,0x30,0x60,0xC0,0x60,0x30,0x18,0x0C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x60,0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x60,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0x0C,0x18,0x18,0x18,0x00,0x18,0x18,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x7C,0xC6,0xC6,0xDE,0xDE,0xDE,0xDC,0xC0,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x10,0x38,0x6C,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x66,0x66,0x66,0x66,0xFC,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xC0,0xC0,0xC2,0x66,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xF8,0x6C,0x66,0x66,0x66,0x66,0x66,0x66,0x6C,0xF8,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0x66,0x62,0x68,0x78,0x68,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x66,0xC2,0xC0,0xC0,0xDE,0xC6,0xC6,0x66,0x3A,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xFE,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x1E,0x0C,0x0C,0x0C,0x0C,0x0C,0xCC,0xCC,0xCC,0x78,0x00,0x00,0x00,0x00},
{0x00,0x00,0xE6,0x66,0x66,0x6C,0x78,0x78,0x6C,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xF0,0x60,0x60,0x60,0x60,0x60,0x60,0x62,0x66,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xEE,0xFE,0xFE,0xD6,0xC6,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xE6,0xF6,0xFE,0xDE,0xCE,0xC6,0xC6,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x60,0x60,0x60,0x60,0xF0,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xD6,0xDE,0x7C,0x0C,0x0E,0x00,0x00},
{0x00,0x00,0xFC,0x66,0x66,0x66,0x7C,0x6C,0x66,0x66,0x66,0xE6,0x00,0x00,0x00,0x00},
{0x00,0x00,0x7C,0xC6,0xC6,0x60,0x38,0x0C,0x06,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFF,0xDB,0x99,0x18,0x18,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x7C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0xC6,0x6C,0x38,0x10,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0xC6,0xD6,0xD6,0xD6,0xFE,0xEE,0x6C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0x6C,0x7C,0x38,0x38,0x7C,0x6C,0xC6,0xC6,0x00,0x00,0x00,0x00},
{0x00,0x00,0xC6,0xC6,0xC6,0x6C,0x38,0x18,0x18,0x18,0x18,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0xFE,0xC6,0x86,0x0C,0x18,0x30,0x60,0xC2,0xC6,0xFE,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x3C,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x80,0xC0,0xE0,0x70,0x38,0x1C,0x0E,0x06,0x02,0x00,0x00,0x00,0x00},
{0x00,0x00,0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00,0x00,0x00,0x00},
{0x10,0x38,0x6C,0xC6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00},
};
static Shader* g_font_shader = NULL;
static ID3D11Buffer* g_font_vb = NULL;
static ID3D11Buffer* g_font_ib = NULL;
static FontVert g_font_verts[FONT_MAX_QUADS * 4];
static uint32_t g_font_indices[FONT_MAX_QUADS * 6];
static int g_font_vi = 0;
static int g_font_ii = 0;
static void make_ortho(float* m, float sw, float sh) {
memset(m, 0, 64);
m[0] = 2.0f / sw;
m[5] = -2.0f / sh;
m[10] = 1.0f;
m[12] = -1.0f;
m[13] = 1.0f;
m[15] = 1.0f;
}
static void make_atlas(uint8_t* pixels, int w, int h) {
memset(pixels, 0, (size_t)w * h * 4);
int cols = w / FONT_CHAR_W;
for (int i = 0; i < FONT_NUM_CHARS; i++) {
int cx = (i % cols) * FONT_CHAR_W;
int cy = (i / cols) * FONT_CHAR_H;
for (int row = 0; row < FONT_CHAR_H; row++) {
uint8_t bits = FONT_DATA[i][row];
for (int col = 0; col < 8; col++) {
if (bits & (0x80 >> col)) {
int px = cx + col, py = cy + row;
int idx = (py * w + px) * 4;
pixels[idx] = 255; pixels[idx+1] = 255; pixels[idx+2] = 255; pixels[idx+3] = 255;
}
}
}
}
}
int bitmap_font_init(BitmapFont* f, ID3D11Device* dev) {
memset(f, 0, sizeof(*f));
int cols = 16;
f->atlas_w = cols * FONT_CHAR_W;
f->atlas_h = ((FONT_NUM_CHARS + cols - 1) / cols) * FONT_CHAR_H;
uint8_t* pixels = (uint8_t*)malloc((size_t)f->atlas_w * f->atlas_h * 4);
make_atlas(pixels, f->atlas_w, f->atlas_h);
D3D11_TEXTURE2D_DESC td = {0};
td.Width = (UINT)f->atlas_w; td.Height = (UINT)f->atlas_h;
td.MipLevels = 1; td.ArraySize = 1;
td.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA sd = {0};
sd.pSysMem = pixels; sd.SysMemPitch = (UINT)(f->atlas_w * 4);
HRESULT hr = dev->lpVtbl->CreateTexture2D(dev, &td, &sd, &f->texture);
free(pixels);
if (FAILED(hr)) return 0;
dev->lpVtbl->CreateShaderResourceView(dev, (ID3D11Resource*)f->texture, NULL, &f->srv);
f->shader = shader_create();
if (!shader_compile(f->shader, dev, NULL, NULL, 0, 0)) return 0;
g_font_shader = f->shader;
D3D11_BUFFER_DESC bd = {0};
bd.ByteWidth = FONT_MAX_QUADS * 4 * sizeof(FontVert);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
memset(g_font_verts, 0, sizeof(g_font_verts));
sd.pSysMem = g_font_verts;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &g_font_vb);
bd.ByteWidth = FONT_MAX_QUADS * 6 * sizeof(uint32_t);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
memset(g_font_indices, 0, sizeof(g_font_indices));
sd.pSysMem = g_font_indices;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &g_font_ib);
bd.ByteWidth = 256; bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
dev->lpVtbl->CreateBuffer(dev, &bd, NULL, &f->cb);
D3D11_BLEND_DESC bdesc = {0};
bdesc.RenderTarget[0].BlendEnable = TRUE;
bdesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bdesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bdesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bdesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bdesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
dev->lpVtbl->CreateBlendState(dev, &bdesc, &f->blend);
D3D11_SAMPLER_DESC sdesc = {0};
sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sdesc.AddressU = sdesc.AddressV = sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
dev->lpVtbl->CreateSamplerState(dev, &sdesc, &f->sampler);
D3D11_DEPTH_STENCIL_DESC dsdesc = {0};
dsdesc.DepthEnable = FALSE;
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dev->lpVtbl->CreateDepthStencilState(dev, &dsdesc, &f->ds_no_depth);
LOG_INFO("bitmap_font: initialized (%dx%d atlas)", f->atlas_w, f->atlas_h);
return 1;
}
void bitmap_font_destroy(BitmapFont* f) {
if (!f) return;
if (f->ds_no_depth) f->ds_no_depth->lpVtbl->Release(f->ds_no_depth);
if (f->sampler) f->sampler->lpVtbl->Release(f->sampler);
if (f->blend) f->blend->lpVtbl->Release(f->blend);
if (f->cb) f->cb->lpVtbl->Release(f->cb);
if (g_font_ib) { g_font_ib->lpVtbl->Release(g_font_ib); g_font_ib = NULL; }
if (g_font_vb) { g_font_vb->lpVtbl->Release(g_font_vb); g_font_vb = NULL; }
if (f->shader) { shader_destroy(f->shader); g_font_shader = NULL; }
if (f->srv) f->srv->lpVtbl->Release(f->srv);
if (f->texture) f->texture->lpVtbl->Release(f->texture);
memset(f, 0, sizeof(*f));
}
static void font_flush(ID3D11DeviceContext* ctx,
float screen_w, float screen_h, int num_idx) {
if (num_idx == 0) return;
float wvp[64];
make_ortho(wvp, screen_w, screen_h);
ID3D11Device* dev = NULL;
ctx->lpVtbl->GetDevice(ctx, &dev);
D3D11_RASTERIZER_DESC rdesc = {0};
rdesc.FillMode = D3D11_FILL_SOLID;
rdesc.CullMode = D3D11_CULL_NONE;
rdesc.ScissorEnable = FALSE;
ID3D11RasterizerState* rstate = NULL;
HRESULT hr = dev->lpVtbl->CreateRasterizerState(dev, &rdesc, &rstate);
if (rstate) {
ctx->lpVtbl->RSSetState(ctx, rstate);
rstate->lpVtbl->Release(rstate);
}
dev->lpVtbl->Release(dev);
shader_bind(g_font_shader, ctx);
shader_set_matrix(g_font_shader, ctx, wvp);
UINT stride = sizeof(FontVert), offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &g_font_vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, g_font_ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->lpVtbl->DrawIndexed(ctx, (UINT)num_idx, 0, 0);
}
static void font_begin(void) { g_font_vi = 0; g_font_ii = 0; }
static void font_add_quad(BitmapFont* f, float x, float y, float w, float h,
float r, float g, float b, float a) {
if (g_font_vi + 4 > FONT_MAX_QUADS * 4) return;
float u0 = 0.19f, v0 = 0.01f, u1 = 0.24f, v1 = 0.15f;
int vi = g_font_vi;
g_font_verts[vi] = (FontVert){{x, y, 0},{0,0,-1},{u0,v0},{r,g,b,a}};
g_font_verts[vi+1] = (FontVert){{x+w, y, 0},{0,0,-1},{u1,v0},{r,g,b,a}};
g_font_verts[vi+2] = (FontVert){{x+w, y+h, 0},{0,0,-1},{u1,v1},{r,g,b,a}};
g_font_verts[vi+3] = (FontVert){{x, y+h, 0},{0,0,-1},{u0,v1},{r,g,b,a}};
uint32_t base = (uint32_t)vi;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+1; g_font_indices[g_font_ii++] = base+2;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+2; g_font_indices[g_font_ii++] = base+3;
g_font_vi += 4;
}
void bitmap_font_draw(BitmapFont* f, ID3D11DeviceContext* ctx,
const char* text, int x, int y,
float r, float g, float b, float a,
float screen_w, float screen_h) {
if (!text || !*text || !f->shader) return;
int len = (int)strlen(text);
if (len > FONT_MAX_QUADS) len = FONT_MAX_QUADS;
int cols = f->atlas_w / FONT_CHAR_W;
font_begin();
int cx = x;
for (int i = 0; i < len; i++) {
int ch = (unsigned char)text[i] - FONT_FIRST_CHAR;
if (ch < 0 || ch >= FONT_NUM_CHARS) ch = 0;
int col_idx = ch % cols;
int row_idx = ch / cols;
float u0 = (float)(col_idx * FONT_CHAR_W) / (float)f->atlas_w;
float v0 = (float)(row_idx * FONT_CHAR_H) / (float)f->atlas_h;
float u1 = (float)((col_idx + 1) * FONT_CHAR_W) / (float)f->atlas_w;
float v1 = (float)((row_idx + 1) * FONT_CHAR_H) / (float)f->atlas_h;
int vi = g_font_vi;
g_font_verts[vi] = (FontVert){{(float)cx,(float)y,0},{0,0,-1},{u0,v0},{r,g,b,a}};
g_font_verts[vi+1] = (FontVert){{(float)cx+FONT_CHAR_W,(float)y,0},{0,0,-1},{u1,v0},{r,g,b,a}};
g_font_verts[vi+2] = (FontVert){{(float)cx+FONT_CHAR_W,(float)y+FONT_CHAR_H,0},{0,0,-1},{u1,v1},{r,g,b,a}};
g_font_verts[vi+3] = (FontVert){{(float)cx,(float)y+FONT_CHAR_H,0},{0,0,-1},{u0,v1},{r,g,b,a}};
uint32_t base = (uint32_t)vi;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+1; g_font_indices[g_font_ii++] = base+2;
g_font_indices[g_font_ii++] = base; g_font_indices[g_font_ii++] = base+2; g_font_indices[g_font_ii++] = base+3;
g_font_vi += 4;
cx += FONT_CHAR_W;
}
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_vb, 0, NULL, g_font_verts, 0, 0);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_ib, 0, NULL, g_font_indices, 0, 0);
font_flush(ctx, screen_w, screen_h, g_font_ii);
}
void bitmap_font_rect(BitmapFont* f, ID3D11DeviceContext* ctx,
float x, float y, float w, float h,
float r, float g, float b, float a,
float screen_w, float screen_h) {
if (!f->shader) return;
font_begin();
font_add_quad(f, x, y, w, h, r, g, b, a);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_vb, 0, NULL, g_font_verts, 0, 0);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)g_font_ib, 0, NULL, g_font_indices, 0, 0);
font_flush(ctx, screen_w, screen_h, g_font_ii);
}
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
#include "../Rendering/shader.h"
#define FONT_CHAR_W 8
#define FONT_CHAR_H 16
#define FONT_FIRST_CHAR 32
#define FONT_NUM_CHARS 96
#define FONT_MAX_QUADS 4096
typedef struct {
float pos[3];
float norm[3];
float uv[2];
float color[4];
} FontVert;
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
ID3D11Buffer* cb;
Shader* shader;
ID3D11BlendState* blend;
ID3D11SamplerState* sampler;
ID3D11DepthStencilState* ds_no_depth;
int atlas_w;
int atlas_h;
} BitmapFont;
int bitmap_font_init(BitmapFont* f, ID3D11Device* dev);
void bitmap_font_destroy(BitmapFont* f);
void bitmap_font_draw(BitmapFont* f, ID3D11DeviceContext* ctx,
const char* text, int x, int y,
float r, float g, float b, float a,
float screen_w, float screen_h);
void bitmap_font_rect(BitmapFont* f, ID3D11DeviceContext* ctx,
float x, float y, float w, float h,
float r, float g, float b, float a,
float screen_w, float screen_h);
+122
View File
@@ -0,0 +1,122 @@
#include "editor.h"
#include "../Core/logger.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static const float BG_DARK[] = {0.12f, 0.12f, 0.14f};
static const float BG_PANEL[] = {0.16f, 0.16f, 0.18f};
static const float BG_TITLE[] = {0.20f, 0.20f, 0.24f};
static const float BG_INPUT[] = {0.10f, 0.10f, 0.12f};
int editor_init(Editor* ed, GraphicsDevice* dev) {
memset(ed, 0, sizeof(*ed));
ed->device = dev;
if (!uilayer_init(&ed->ui, dev->device, (float)dev->width, (float)dev->height)) return 0;
int sw = dev->width, sh = dev->height;
int menu_h = 28;
ed->panels[0] = (EditorPanel){PANEL_VIEWPORT, "Viewport", 220, menu_h, sw-440, sh-menu_h-200, 1, 0, 0};
ed->panels[1] = (EditorPanel){PANEL_SCENE, "Scene", 0, menu_h, 220, sh-menu_h-200, 1, 0, 0};
ed->panels[2] = (EditorPanel){PANEL_INSPECTOR, "Inspector", sw-220, menu_h, 220, sh-menu_h-200, 1, 0, 0};
ed->panels[3] = (EditorPanel){PANEL_CONSOLE, "Console", 0, sh-200, sw, 200, 1, 0, 0};
ed->panel_count = 4;
ed->scene_entities_count = 8;
strcpy(ed->selected_entity, "Cube");
LOG_INFO("Editor initialized");
return 1;
}
void editor_shutdown(Editor* ed) {
uilayer_destroy(&ed->ui);
}
void editor_update(Editor* ed, float dt) {
ed->time += dt;
ed->viewport_rotation += dt * 0.5f;
}
void editor_render(Editor* ed) {
ID3D11DeviceContext* ctx = ed->device->context;
float sw = (float)ed->device->width;
float sh = (float)ed->device->height;
graphics_device_clear(ed->device, BG_DARK[0], BG_DARK[1], BG_DARK[2], 1.0f);
uilayer_begin(&ed->ui);
uilayer_rect(&ed->ui, 0, 0, sw, 28, 1.0f, 0.0f, 0.0f, 1.0f);
uilayer_rect(&ed->ui, 0, 28, sw, 28, 0.0f, 1.0f, 0.0f, 1.0f);
uilayer_rect(&ed->ui, 0, 56, sw/3, sh-256, 0.0f, 0.0f, 1.0f, 1.0f);
uilayer_rect(&ed->ui, sw/3, 56, sw/3, sh-256, 1.0f, 1.0f, 0.0f, 1.0f);
uilayer_rect(&ed->ui, 2*sw/3, 56, sw/3, sh-256, 1.0f, 0.0f, 1.0f, 1.0f);
uilayer_rect(&ed->ui, 0, sh-200, sw, 200, 0.0f, 1.0f, 1.0f, 1.0f);
uilayer_rect(&ed->ui, 0, sh-22, sw, 22, 1.0f, 0.5f, 0.0f, 1.0f);
uilayer_flush(&ed->ui, ctx);
float sw2 = sw, sh2 = sh;
bitmap_font_draw(&ed->font, ctx, "Lity3D Engine Editor", 10, 6,
0.4f, 0.75f, 1.0f, 1.0f, sw2, sh2);
const char* menu[] = {"File", "Edit", "View", "Build", "Help"};
int mx = 180;
for (int i = 0; i < 5; i++) {
bitmap_font_draw(&ed->font, ctx, menu[i], mx + 8, 6,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
mx += (int)strlen(menu[i]) * 8 + 16;
}
const char* tools[] = {"Select", "Move", "Rotate", "Scale"};
int tx = 6;
for (int i = 0; i < 4; i++) {
bitmap_font_draw(&ed->font, ctx, tools[i], tx + 4, 34,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
tx += (int)strlen(tools[i]) * 8 + 16;
}
for (int i = 0; i < ed->panel_count; i++) {
EditorPanel* p = &ed->panels[i];
if (!p->visible) continue;
bitmap_font_draw(&ed->font, ctx, p->title, p->x + 6, p->y + 6,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
}
bitmap_font_draw(&ed->font, ctx, ed->selected_entity, 226, 56,
0.4f, 0.75f, 1.0f, 1.0f, sw2, sh2);
const char* entities[] = {"Light", "Camera", "Cube", "Plane", "Sphere", "Particles", "Audio", "Skybox"};
for (int i = 0; i < 8; i++) {
bitmap_font_draw(&ed->font, ctx, entities[i], 226, 78 + i * 22,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
}
const char* sections[] = {"Transform", "Mesh Filter", "Material", "Collider"};
int sy = 56;
for (int s = 0; s < 4; s++) {
bitmap_font_draw(&ed->font, ctx, sections[s], (int)sw - 214, sy,
0.9f, 0.9f, 0.92f, 1.0f, sw2, sh2);
sy += 26;
}
char status[128];
snprintf(status, sizeof(status), "Entities: %d | Time: %.1fs", ed->scene_entities_count, ed->time);
bitmap_font_draw(&ed->font, ctx, status, 10, (int)sh - 18,
0.55f, 0.55f, 0.6f, 1.0f, sw2, sh2);
}
void editor_handle_input(Editor* ed, int msg, int wp, int lp) {
(void)lp;
switch (msg) {
case 0x0200: ed->input.mouse_x = wp & 0xFFFF; ed->input.mouse_y = (wp >> 16) & 0x7FFF; break;
case 0x0201: ed->input.mouse_lbtn = 1; break;
case 0x0202: ed->input.mouse_lbtn = 0; break;
case 0x0204: ed->input.mouse_rbtn = 1; break;
case 0x0205: ed->input.mouse_rbtn = 0; break;
case 0x0100: if (wp < 256) ed->input.key_down[wp] = 1; break;
case 0x0101: if (wp < 256) ed->input.key_down[wp] = 0; break;
}
}
+55
View File
@@ -0,0 +1,55 @@
#pragma once
#include "uilayer.h"
#include "bitmap_font.h"
#include "../Rendering/graphics_device.h"
#include <stdint.h>
#define EDITOR_MAX_PANELS 16
typedef enum {
PANEL_SCENE,
PANEL_INSPECTOR,
PANEL_CONSOLE,
PANEL_ASSETS,
PANEL_VIEWPORT,
} PanelType;
typedef struct {
PanelType type;
const char* title;
int x, y, w, h;
int visible;
int collapsed;
int active_tab;
} EditorPanel;
typedef struct {
int mouse_x, mouse_y;
int mouse_lbtn;
int mouse_rbtn;
int key_down[256];
char text_input[256];
int text_len;
} EditorInput;
typedef struct {
GraphicsDevice* device;
UILayer ui;
BitmapFont font;
EditorPanel panels[EDITOR_MAX_PANELS];
int panel_count;
EditorInput input;
float time;
char console_lines[32][256];
int console_count;
int console_scroll;
char selected_entity[128];
int scene_entities_count;
float viewport_rotation;
} Editor;
int editor_init(Editor* ed, GraphicsDevice* dev);
void editor_shutdown(Editor* ed);
void editor_update(Editor* ed, float dt);
void editor_render(Editor* ed);
void editor_handle_input(Editor* ed, int msg, int wp, int lp);
+124
View File
@@ -0,0 +1,124 @@
#include "editor_2d.h"
#include <stdlib.h>
#include <string.h>
int editor2d_init(Editor2D* e, ID3D11Device* dev) {
memset(e, 0, sizeof(*e));
e->shader = shader_create();
if (!shader_compile(e->shader, dev, NULL, NULL, 0, 0)) return 0;
D3D11_BUFFER_DESC bd = {0};
bd.ByteWidth = ED2D_MAX_QUADS * 4 * sizeof(Ed2DVert);
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
if (FAILED(dev->lpVtbl->CreateBuffer(dev, &bd, NULL, &e->vb))) return 0;
bd.ByteWidth = ED2D_MAX_QUADS * 6 * sizeof(uint32_t);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
uint32_t* idx = (uint32_t*)malloc(ED2D_MAX_QUADS * 6 * sizeof(uint32_t));
for (int i = 0; i < ED2D_MAX_QUADS; i++) {
uint32_t b2 = i * 4;
idx[i*6]=b2; idx[i*6+1]=b2+1; idx[i*6+2]=b2+2;
idx[i*6+3]=b2; idx[i*6+4]=b2+2; idx[i*6+5]=b2+3;
}
D3D11_SUBRESOURCE_DATA sd = {0}; sd.pSysMem = idx;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &e->ib);
free(idx);
e->verts = (Ed2DVert*)malloc(ED2D_MAX_QUADS * 4 * sizeof(Ed2DVert));
D3D11_BLEND_DESC bdesc = {0};
bdesc.RenderTarget[0].BlendEnable = TRUE;
bdesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bdesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bdesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bdesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bdesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
dev->lpVtbl->CreateBlendState(dev, &bdesc, &e->blend);
D3D11_DEPTH_STENCIL_DESC dsdesc = {0};
dsdesc.DepthEnable = FALSE;
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dev->lpVtbl->CreateDepthStencilState(dev, &dsdesc, &e->ds_no_depth);
return 1;
}
void editor2d_destroy(Editor2D* e) {
if (!e) return;
if (e->ds_no_depth) e->ds_no_depth->lpVtbl->Release(e->ds_no_depth);
if (e->blend) e->blend->lpVtbl->Release(e->blend);
if (e->ib) e->ib->lpVtbl->Release(e->ib);
if (e->vb) e->vb->lpVtbl->Release(e->vb);
shader_destroy(e->shader);
free(e->verts);
memset(e, 0, sizeof(*e));
}
void editor2d_begin(Editor2D* e, float sw, float sh) {
e->quad_count = 0;
e->screen_w = sw;
e->screen_h = sh;
memset(e->wvp, 0, sizeof(e->wvp));
e->wvp[0] = 2.0f / sw;
e->wvp[5] = -2.0f / sh;
e->wvp[12] = -1.0f;
e->wvp[13] = 1.0f;
e->wvp[15] = 1.0f;
}
void editor2d_rect(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a) {
if (e->quad_count >= ED2D_MAX_QUADS) return;
int vi = e->quad_count * 4;
e->verts[vi+0] = (Ed2DVert){x, y, 0, 0,0,-1, 0,1, r,g,b,a};
e->verts[vi+1] = (Ed2DVert){x+w, y, 0, 0,0,-1, 1,1, r,g,b,a};
e->verts[vi+2] = (Ed2DVert){x+w, y+h, 0, 0,0,-1, 1,0, r,g,b,a};
e->verts[vi+3] = (Ed2DVert){x, y+h, 0, 0,0,-1, 0,0, r,g,b,a};
e->quad_count++;
}
void editor2d_rect_border(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a, float t) {
editor2d_rect(e, x, y, w, t, r, g, b, a);
editor2d_rect(e, x, y+h-t, w, t, r, g, b, a);
editor2d_rect(e, x, y, t, h, r, g, b, a);
editor2d_rect(e, x+w-t, y, t, h, r, g, b, a);
}
void editor2d_end(Editor2D* e, ID3D11DeviceContext* ctx) {
if (e->quad_count == 0) return;
D3D11_MAPPED_SUBRESOURCE mapped;
if (FAILED(ctx->lpVtbl->Map(ctx, (ID3D11Resource*)e->vb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) return;
memcpy(mapped.pData, e->verts, (size_t)e->quad_count * 4 * sizeof(Ed2DVert));
ctx->lpVtbl->Unmap(ctx, (ID3D11Resource*)e->vb, 0);
if (e->rtv) {
ctx->lpVtbl->OMSetRenderTargets(ctx, 1, &e->rtv, NULL);
}
D3D11_VIEWPORT vp = {0};
vp.Width = e->screen_w;
vp.Height = e->screen_h;
vp.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
shader_bind(e->shader, ctx);
shader_set_matrix(e->shader, ctx, e->wvp);
UINT stride = sizeof(Ed2DVert), offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &e->vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, e->ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
float bf[4] = {0};
ctx->lpVtbl->OMSetBlendState(ctx, e->blend, bf, 0xFFFFFFFF);
ctx->lpVtbl->OMSetDepthStencilState(ctx, e->ds_no_depth, 0);
ctx->lpVtbl->DrawIndexed(ctx, (UINT)(e->quad_count * 6), 0, 0);
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
#include "../Rendering/shader.h"
#include "../Rendering/mesh.h"
#define ED2D_MAX_QUADS 8192
typedef struct {
float x, y, z;
float nx, ny, nz;
float u, v;
float r, g, b, a;
} Ed2DVert;
typedef struct {
Shader* shader;
ID3D11Buffer* vb;
ID3D11Buffer* ib;
ID3D11BlendState* blend;
ID3D11DepthStencilState* ds_no_depth;
ID3D11RenderTargetView* rtv;
Ed2DVert* verts;
int quad_count;
float screen_w;
float screen_h;
float wvp[16];
} Editor2D;
int editor2d_init(Editor2D* e, ID3D11Device* dev);
void editor2d_destroy(Editor2D* e);
void editor2d_begin(Editor2D* e, float sw, float sh);
void editor2d_rect(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a);
void editor2d_rect_border(Editor2D* e, float x, float y, float w, float h, float r, float g, float b, float a, float thickness);
void editor2d_end(Editor2D* e, ID3D11DeviceContext* ctx);
+171
View File
@@ -0,0 +1,171 @@
#include "glyph_atlas.h"
#include <stdlib.h>
#include <string.h>
#include <d3d11.h>
#include <Windows.h>
#include "../Core/logger.h"
typedef struct {
wchar_t ch;
int x, y;
int w, h;
float u0, v0, u1, v1;
float advance_x;
} GlyphEntry;
#define MAX_GLYPHS 1024
static GlyphEntry g_entries[MAX_GLYPHS];
static int g_entry_count = 0;
int glyph_atlas_init(GlyphAtlas* ga, ID3D11Device* dev) {
memset(ga, 0, sizeof(*ga));
ga->cell_w = GLYPH_CELL_W;
ga->cell_h = GLYPH_CELL_H;
ga->atlas_w = GLYPH_ATLAS_SIZE;
ga->atlas_h = GLYPH_ATLAS_SIZE;
ga->next_x = 0;
ga->next_y = 0;
ga->u_scale = 1.0f / (float)ga->atlas_w;
ga->v_scale = 1.0f / (float)ga->atlas_h;
uint8_t* pixels = (uint8_t*)calloc(ga->atlas_w * ga->atlas_h * 4, 1);
D3D11_TEXTURE2D_DESC td = {0};
td.Width = ga->atlas_w;
td.Height = ga->atlas_h;
td.MipLevels = 1;
td.ArraySize = 1;
td.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
td.SampleDesc.Count = 1;
td.Usage = D3D11_USAGE_DEFAULT;
td.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA sd = {0};
sd.pSysMem = pixels;
sd.SysMemPitch = ga->atlas_w * 4;
HRESULT hr = dev->lpVtbl->CreateTexture2D(dev, &td, &sd, &ga->texture);
free(pixels);
if (FAILED(hr)) return 0;
dev->lpVtbl->CreateShaderResourceView(dev, (ID3D11Resource*)ga->texture, NULL, &ga->srv);
LOG_INFO("GlyphAtlas: initialized (%dx%d, max %d glyphs)", ga->atlas_w, ga->atlas_h, MAX_GLYPHS);
return 1;
}
void glyph_atlas_destroy(GlyphAtlas* ga) {
if (!ga) return;
if (ga->srv) ga->srv->lpVtbl->Release(ga->srv);
if (ga->texture) ga->texture->lpVtbl->Release(ga->texture);
memset(ga, 0, sizeof(*ga));
g_entry_count = 0;
}
static int find_glyph(wchar_t ch) {
for (int i = 0; i < g_entry_count; i++) {
if (g_entries[i].ch == ch) return i;
}
return -1;
}
int glyph_atlas_get_or_create(GlyphAtlas* ga, ID3D11Device* dev, ID3D11DeviceContext* ctx, wchar_t ch,
float* u0, float* v0, float* u1, float* v1,
float* advance_x, float* advance_y) {
int idx = find_glyph(ch);
if (idx >= 0) {
*u0 = g_entries[idx].u0;
*v0 = g_entries[idx].v0;
*u1 = g_entries[idx].u1;
*v1 = g_entries[idx].v1;
*advance_x = g_entries[idx].advance_x;
*advance_y = (float)g_entries[idx].h;
return 1;
}
if (g_entry_count >= MAX_GLYPHS) return 0;
if (ga->next_x + ga->cell_w > ga->atlas_w) {
ga->next_x = 0;
ga->next_y += ga->cell_h;
}
if (ga->next_y + ga->cell_h > ga->atlas_h) return 0;
HDC hdc = GetDC(NULL);
HFONT hfont = CreateFontW(
ga->cell_h - 4, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, FIXED_PITCH | FF_DONTCARE, L"Microsoft YaHei"
);
HFONT old = (HFONT)SelectObject(hdc, hfont);
GLYPHMETRICS gm = {0};
MAT2 mat = {{0,0},{0,0},{0,0},{0,0}};
mat.eM11.value = 1; mat.eM22.value = 1;
DWORD size = GetGlyphOutlineW(hdc, ch, GGO_GRAY8_BITMAP, &gm, 0, NULL, &mat);
int gw = gm.gmBlackBoxX;
int gh = gm.gmBlackBoxY;
int gx = ga->next_x;
int gy = ga->next_y;
if (size > 0 && gw > 0 && gh > 0) {
uint8_t* buf = (uint8_t*)malloc(size);
GetGlyphOutlineW(hdc, ch, GGO_GRAY8_BITMAP, &gm, size, buf, &mat);
uint8_t* pixels = (uint8_t*)calloc(gw * gh * 4, 1);
int row_bytes = ((gw + 3) / 4) * 4;
for (int row = 0; row < gh; row++) {
for (int col = 0; col < gw; col++) {
int src_idx = row * row_bytes + col;
if (src_idx < (int)size) {
uint8_t gray = buf[src_idx];
uint8_t alpha = (gray > 200) ? 255 : (gray > 100) ? 200 : (gray > 30) ? 128 : 0;
int idx = (row * gw + col) * 4;
pixels[idx] = 255;
pixels[idx + 1] = 255;
pixels[idx + 2] = 255;
pixels[idx + 3] = alpha;
}
}
}
D3D11_BOX box = {0};
box.left = gx;
box.top = gy;
box.front = 0;
box.right = gx + gw;
box.bottom = gy + gh;
box.back = 1;
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)ga->texture, 0, &box, pixels, gw * 4, 0);
free(buf);
free(pixels);
}
SelectObject(hdc, old);
DeleteObject(hfont);
ReleaseDC(NULL, hdc);
GlyphEntry* e = &g_entries[g_entry_count++];
e->ch = ch;
e->x = gx;
e->y = gy;
e->w = gw > 0 ? gw : ga->cell_w / 2;
e->h = gh > 0 ? gh : ga->cell_h;
e->u0 = (float)gx * ga->u_scale;
e->v0 = (float)gy * ga->v_scale;
e->u1 = (float)(gx + e->w) * ga->u_scale;
e->v1 = (float)(gy + e->h) * ga->v_scale;
e->advance_x = (float)(gm.gmCellIncX > 0 ? gm.gmCellIncX : ga->cell_w / 2);
ga->next_x += ga->cell_w;
*u0 = e->u0;
*v0 = e->v0;
*u1 = e->u1;
*v1 = e->v1;
*advance_x = e->advance_x;
*advance_y = (float)e->h;
return 1;
}
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
#define GLYPH_CELL_W 32
#define GLYPH_CELL_H 32
#define GLYPH_ATLAS_COLS 16
#define GLYPH_ATLAS_ROWS 16
#define GLYPH_ATLAS_SIZE (GLYPH_ATLAS_COLS * GLYPH_CELL_W)
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
int atlas_w;
int atlas_h;
int next_x;
int next_y;
int cell_w;
int cell_h;
float u_scale;
float v_scale;
} GlyphAtlas;
int glyph_atlas_init(GlyphAtlas* ga, ID3D11Device* dev);
void glyph_atlas_destroy(GlyphAtlas* ga);
int glyph_atlas_get_or_create(GlyphAtlas* ga, ID3D11Device* dev, ID3D11DeviceContext* ctx, wchar_t ch,
float* u0, float* v0, float* u1, float* v1,
float* advance_x, float* advance_y);
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "uilayer.h"
#include "bitmap_font.h"
int textlayer_init(TextLayer* t, ID3D11Device* dev, BitmapFont* font, UILayer* ui);
void textlayer_destroy(TextLayer* t);
void textlayer_draw(TextLayer* t, ID3D11DeviceContext* ctx,
const char* text, int x, int y,
float r, float g, float b, float a,
float screen_w, float screen_h);
+252
View File
@@ -0,0 +1,252 @@
#include "uilayer.h"
#include <stdlib.h>
#include <string.h>
#include "../Core/logger.h"
#define UI_MAX_VERTS (UI_MAX_QUADS * 4)
#define UI_MAX_IDXS (UI_MAX_QUADS * 6)
typedef struct { float p[3], n[3], uv[2], c[4]; } UIVert;
static UIVert g_verts[UI_MAX_VERTS];
static uint32_t g_indices[UI_MAX_IDXS];
static int g_vi = 0;
static int g_ii = 0;
static UIVert g_text_verts[UI_MAX_VERTS];
static uint32_t g_text_indices[UI_MAX_IDXS];
static int g_tvi = 0;
static int g_tii = 0;
static ID3D11ShaderResourceView* g_text_srv = NULL;
static void screen_to_world(float sx, float sy, float sw, float sh, float z, float* wx, float* wy) {
*wx = sx - sw * 0.5f;
*wy = -(sy - sh * 0.5f);
}
int uilayer_init(UILayer* u, ID3D11Device* dev, float screen_w, float screen_h) {
memset(u, 0, sizeof(*u));
u->screen_w = screen_w;
u->screen_h = screen_h;
u->z_plane = 0.0f;
u->camera = camera_orthographic(screen_w, screen_h, 0.1f, 200.0f);
u->camera.position = (Vec3){0, 0, 100};
u->camera.target = (Vec3){0, 0, 0};
u->camera.dirty = 1;
camera_update(&u->camera);
u->shader = shader_create();
if (!shader_compile(u->shader, dev, NULL, NULL, 0, 0)) {
LOG_ERROR("UILayer: shader compile failed");
return 0;
}
D3D11_BUFFER_DESC bd = {0};
D3D11_SUBRESOURCE_DATA sd = {0};
UIVert* empty = (UIVert*)calloc(UI_MAX_VERTS, sizeof(UIVert));
bd.ByteWidth = UI_MAX_VERTS * sizeof(UIVert);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = empty;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->vb);
free(empty);
uint32_t* eidx = (uint32_t*)calloc(UI_MAX_IDXS, sizeof(uint32_t));
bd.ByteWidth = UI_MAX_IDXS * sizeof(uint32_t);
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = eidx;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->ib);
free(eidx);
UIVert* empty2 = (UIVert*)calloc(UI_MAX_VERTS, sizeof(UIVert));
bd.ByteWidth = UI_MAX_VERTS * sizeof(UIVert);
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = empty2;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->text_vb);
free(empty2);
uint32_t* eidx2 = (uint32_t*)calloc(UI_MAX_IDXS, sizeof(uint32_t));
bd.ByteWidth = UI_MAX_IDXS * sizeof(uint32_t);
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = eidx2;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &u->text_ib);
free(eidx2);
D3D11_RASTERIZER_DESC rdesc = {0};
rdesc.FillMode = D3D11_FILL_SOLID;
rdesc.CullMode = D3D11_CULL_NONE;
dev->lpVtbl->CreateRasterizerState(dev, &rdesc, &u->rstate);
D3D11_DEPTH_STENCIL_DESC dsdesc = {0};
dsdesc.DepthEnable = FALSE;
dsdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
dev->lpVtbl->CreateDepthStencilState(dev, &dsdesc, &u->ds_state);
D3D11_BLEND_DESC bdesc = {0};
bdesc.RenderTarget[0].BlendEnable = TRUE;
bdesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bdesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bdesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bdesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bdesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bdesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
dev->lpVtbl->CreateBlendState(dev, &bdesc, &u->text_blend);
D3D11_SAMPLER_DESC sdesc = {0};
sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sdesc.AddressU = sdesc.AddressV = sdesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
dev->lpVtbl->CreateSamplerState(dev, &sdesc, &u->text_sampler);
const char* text_vs_src =
"cbuffer CB:register(b0){float4x4 WVP;};"
"struct VS_IN{float4 pos:POSITION;float3 n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
"struct PS_IN{float4 pos:SV_POSITION;float2 uv:TEXCOORD;float4 col:COLOR;};"
"PS_IN main(VS_IN i){PS_IN o;o.pos=mul(i.pos,WVP);o.uv=i.uv;o.col=i.col;return o;}";
const char* text_ps_src =
"Texture2D tex:register(t0);SamplerState smp:register(s0);"
"struct PS_IN{float4 pos:SV_POSITION;float2 uv:TEXCOORD;float4 col:COLOR;};"
"float4 main(PS_IN i):SV_TARGET{float a=tex.Sample(smp,i.uv).a;return float4(i.col.rgb,i.col.a*a);}";
ID3DBlob *vsb = NULL, *psb = NULL;
D3DCompile(text_vs_src, (UINT)strlen(text_vs_src), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vsb, NULL);
D3DCompile(text_ps_src, (UINT)strlen(text_ps_src), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &psb, NULL);
if (vsb && psb) {
dev->lpVtbl->CreateVertexShader(dev, vsb->lpVtbl->GetBufferPointer(vsb), vsb->lpVtbl->GetBufferSize(vsb), NULL, &u->text_vs);
dev->lpVtbl->CreatePixelShader(dev, psb->lpVtbl->GetBufferPointer(psb), psb->lpVtbl->GetBufferSize(psb), NULL, &u->text_ps);
D3D11_INPUT_ELEMENT_DESC layout[] = {
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
{"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0},
{"TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,24,D3D11_INPUT_PER_VERTEX_DATA,0},
{"COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,32,D3D11_INPUT_PER_VERTEX_DATA,0},
};
dev->lpVtbl->CreateInputLayout(dev, layout, 4, vsb->lpVtbl->GetBufferPointer(vsb), vsb->lpVtbl->GetBufferSize(vsb), &u->text_layout);
vsb->lpVtbl->Release(vsb);
psb->lpVtbl->Release(psb);
}
LOG_INFO("UILayer: initialized (%.0fx%.0f)", screen_w, screen_h);
return 1;
}
void uilayer_destroy(UILayer* u) {
if (!u) return;
if (u->text_sampler) u->text_sampler->lpVtbl->Release(u->text_sampler);
if (u->text_blend) u->text_blend->lpVtbl->Release(u->text_blend);
if (u->text_layout) u->text_layout->lpVtbl->Release(u->text_layout);
if (u->text_ps) u->text_ps->lpVtbl->Release(u->text_ps);
if (u->text_vs) u->text_vs->lpVtbl->Release(u->text_vs);
if (u->text_ib) u->text_ib->lpVtbl->Release(u->text_ib);
if (u->text_vb) u->text_vb->lpVtbl->Release(u->text_vb);
if (u->ds_state) u->ds_state->lpVtbl->Release(u->ds_state);
if (u->rstate) u->rstate->lpVtbl->Release(u->rstate);
if (u->ib) u->ib->lpVtbl->Release(u->ib);
if (u->vb) u->vb->lpVtbl->Release(u->vb);
if (u->shader) shader_destroy(u->shader);
memset(u, 0, sizeof(*u));
}
void uilayer_begin(UILayer* u) {
(void)u;
g_vi = 0; g_ii = 0;
g_tvi = 0; g_tii = 0;
g_text_srv = NULL;
}
void uilayer_rect(UILayer* u, float x, float y, float w, float h,
float r, float g, float b, float a) {
if (g_vi + 4 > UI_MAX_VERTS) return;
float x0, y0, x1, y1;
screen_to_world(x, y, u->screen_w, u->screen_h, u->z_plane, &x0, &y0);
screen_to_world(x + w, y + h, u->screen_w, u->screen_h, u->z_plane, &x1, &y1);
float z = u->z_plane;
uint32_t base = (uint32_t)g_vi;
g_verts[g_vi] = (UIVert){{x0,y0,z},{0,0,1},{0,0},{r,g,b,a}}; g_vi++;
g_verts[g_vi] = (UIVert){{x1,y0,z},{0,0,1},{1,0},{r,g,b,a}}; g_vi++;
g_verts[g_vi] = (UIVert){{x1,y1,z},{0,0,1},{1,1},{r,g,b,a}}; g_vi++;
g_verts[g_vi] = (UIVert){{x0,y1,z},{0,0,1},{0,1},{r,g,b,a}}; g_vi++;
g_indices[g_ii++] = base; g_indices[g_ii++] = base+1; g_indices[g_ii++] = base+2;
g_indices[g_ii++] = base; g_indices[g_ii++] = base+2; g_indices[g_ii++] = base+3;
}
void uilayer_textured_rect(UILayer* u, ID3D11DeviceContext* ctx,
ID3D11ShaderResourceView* srv,
float x, float y, float w, float h,
float u0, float v0, float u1, float v1,
float r, float g, float b, float a) {
(void)ctx;
if (!srv || g_tvi + 4 > UI_MAX_VERTS) return;
float x0, y0, x1, y1;
screen_to_world(x, y, u->screen_w, u->screen_h, u->z_plane, &x0, &y0);
screen_to_world(x + w, y + h, u->screen_w, u->screen_h, u->z_plane, &x1, &y1);
float z = u->z_plane;
uint32_t base = (uint32_t)g_tvi;
g_text_verts[g_tvi] = (UIVert){{x0,y0,z},{0,0,1},{u0,v0},{r,g,b,a}}; g_tvi++;
g_text_verts[g_tvi] = (UIVert){{x1,y0,z},{0,0,1},{u1,v0},{r,g,b,a}}; g_tvi++;
g_text_verts[g_tvi] = (UIVert){{x1,y1,z},{0,0,1},{u1,v1},{r,g,b,a}}; g_tvi++;
g_text_verts[g_tvi] = (UIVert){{x0,y1,z},{0,0,1},{u0,v1},{r,g,b,a}}; g_tvi++;
g_text_indices[g_tii++] = base; g_text_indices[g_tii++] = base+1; g_text_indices[g_tii++] = base+2;
g_text_indices[g_tii++] = base; g_text_indices[g_tii++] = base+2; g_text_indices[g_tii++] = base+3;
g_text_srv = srv;
}
static void flush_batch(ID3D11DeviceContext* ctx, UIVert* verts, uint32_t* indices,
int num_idx, ID3D11Buffer* vb, ID3D11Buffer* ib) {
if (num_idx == 0) return;
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)vb, 0, NULL, verts, 0, 0);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)ib, 0, NULL, indices, 0, 0);
UINT stride = sizeof(UIVert), offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->DrawIndexed(ctx, (UINT)num_idx, 0, 0);
}
void uilayer_flush(UILayer* u, ID3D11DeviceContext* ctx) {
if (g_ii == 0) return;
Mat4 view = u->camera.view;
Mat4 proj = u->camera.projection;
Mat4 vp, vp_t;
mat4_multiply(&view, &proj, &vp);
mat4_transpose(&vp, &vp_t);
D3D11_VIEWPORT vport = {0};
vport.Width = u->screen_w; vport.Height = u->screen_h; vport.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vport);
ctx->lpVtbl->RSSetState(ctx, u->rstate);
ctx->lpVtbl->OMSetDepthStencilState(ctx, u->ds_state, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
shader_bind(u->shader, ctx);
shader_set_matrix(u->shader, ctx, (float*)&vp_t);
flush_batch(ctx, g_verts, g_indices, g_ii, u->vb, u->ib);
}
void uilayer_flush_text(UILayer* u, ID3D11DeviceContext* ctx) {
if (g_tii == 0 || !g_text_srv || !u->text_vs || !u->text_ps) return;
Mat4 view = u->camera.view;
Mat4 proj = u->camera.projection;
Mat4 vp, vp_t;
mat4_multiply(&view, &proj, &vp);
mat4_transpose(&vp, &vp_t);
D3D11_VIEWPORT vport = {0};
vport.Width = u->screen_w; vport.Height = u->screen_h; vport.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vport);
ctx->lpVtbl->RSSetState(ctx, u->rstate);
ctx->lpVtbl->OMSetDepthStencilState(ctx, u->ds_state, 0);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->lpVtbl->VSSetShader(ctx, u->text_vs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, u->text_ps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, u->text_layout);
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)u->shader->cb, 0, NULL, &vp_t, 0, 0);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &u->shader->cb);
float bf[4] = {0};
ctx->lpVtbl->OMSetBlendState(ctx, u->text_blend, bf, 0xFFFFFFFF);
ctx->lpVtbl->PSSetShaderResources(ctx, 0, 1, &g_text_srv);
ctx->lpVtbl->PSSetSamplers(ctx, 0, 1, &u->text_sampler);
flush_batch(ctx, g_text_verts, g_text_indices, g_tii, u->text_vb, u->text_ib);
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "../Rendering/camera.h"
#include "../Rendering/shader.h"
#include "../Rendering/mesh.h"
#include "../Rendering/graphics_device.h"
#include "../ASM/math_c_fallback.h"
#define UI_MAX_QUADS 8192
typedef struct {
Camera camera;
Shader* shader;
ID3D11Buffer* vb;
ID3D11Buffer* ib;
ID3D11Buffer* text_vb;
ID3D11Buffer* text_ib;
ID3D11RasterizerState* rstate;
ID3D11DepthStencilState* ds_state;
ID3D11VertexShader* text_vs;
ID3D11PixelShader* text_ps;
ID3D11InputLayout* text_layout;
ID3D11BlendState* text_blend;
ID3D11SamplerState* text_sampler;
float screen_w;
float screen_h;
float z_plane;
} UILayer;
int uilayer_init(UILayer* u, ID3D11Device* dev, float screen_w, float screen_h);
void uilayer_destroy(UILayer* u);
void uilayer_begin(UILayer* u);
void uilayer_rect(UILayer* u,
float x, float y, float w, float h,
float r, float g, float b, float a);
void uilayer_textured_rect(UILayer* u, ID3D11DeviceContext* ctx,
ID3D11ShaderResourceView* srv,
float x, float y, float w, float h,
float u0, float v0, float u1, float v1,
float r, float g, float b, float a);
void uilayer_flush(UILayer* u, ID3D11DeviceContext* ctx);
void uilayer_flush_text(UILayer* u, ID3D11DeviceContext* ctx);
+124
View File
@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{B5C5B5B5-5B5B-5B5B-5B5B-5B5B5B5B5B5B}</ProjectGuid>
<RootNamespace>Engine</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdc17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
</ClCompile>
<Lib>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdc17</LanguageStandard>
<AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<EnableEnhancedInstructionSet>AdvancedVectorExtensions2</EnableEnhancedInstructionSet>
</ClCompile>
<Lib>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<MASM Include="ASM\math.asm" />
<MASM Include="ASM\memory.asm" />
<MASM Include="ASM\string_ops.asm" />
<MASM Include="ASM\profiling.asm" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Core\engine.c" />
<ClCompile Include="Core\logger.c" />
<ClCompile Include="Core\timer.c" />
<ClCompile Include="Window\window.c" />
<ClCompile Include="Rendering\graphics_device.c" />
<ClCompile Include="Rendering\renderer.c" />
<ClCompile Include="Rendering\shader.c" />
<ClCompile Include="Rendering\mesh.c" />
<ClCompile Include="Rendering\texture.c" />
<ClCompile Include="Rendering\camera.c" />
<ClCompile Include="ECS\world.c" />
<ClCompile Include="Input\input.c" />
<ClCompile Include="Physics\physics.c" />
<ClCompile Include="Audio\audio.c" />
<ClCompile Include="UI\ui.c" />
<ClCompile Include="Scripting\lua_engine.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Core\engine.h" />
<ClInclude Include="Core\logger.h" />
<ClInclude Include="Core\timer.h" />
<ClInclude Include="Window\window.h" />
<ClInclude Include="include\engine_api.h" />
<ClInclude Include="include\asm_macros.inc" />
<ClInclude Include="Rendering\graphics_device.h" />
<ClInclude Include="Rendering\renderer.h" />
<ClInclude Include="Rendering\shader.h" />
<ClInclude Include="Rendering\mesh.h" />
<ClInclude Include="Rendering\texture.h" />
<ClInclude Include="Rendering\camera.h" />
<ClInclude Include="ECS\world.h" />
<ClInclude Include="Input\input.h" />
<ClInclude Include="Physics\physics.h" />
<ClInclude Include="Audio\audio.h" />
<ClInclude Include="UI\ui.h" />
<ClInclude Include="Scripting\lua_engine.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
</Project>
+138
View File
@@ -0,0 +1,138 @@
#include "input.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
static float normalize_trigger(BYTE value) {
return value / 255.0f;
}
static float normalize_axis(SHORT value) {
if (value > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
return (value - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) / (32767.0f - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
} else if (value < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) {
return (value + XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) / (32767.0f - XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
}
return 0.0f;
}
Input* input_create(void) {
Input* input = (Input*)calloc(1, sizeof(Input));
input->deadzone = 0.2f;
return input;
}
void input_destroy(Input* input) {
free(input);
}
void input_update(Input* input) {
memcpy(input->key_prev, input->key_states, sizeof(int) * MAX_KEYS);
memcpy(input->mouse_prev, input->mouse_buttons, sizeof(int) * 3);
input->mouse_dx = 0;
input->mouse_dy = 0;
input->mouse_scroll = 0;
memcpy(&input->gamepad_prev, &input->gamepad_state, sizeof(XINPUT_STATE));
input->gamepad_connected = (XInputGetState(0, &input->gamepad_state) == ERROR_SUCCESS);
}
void input_process_key(Input* input, int key, int down) {
if (key >= 0 && key < MAX_KEYS) {
input->key_states[key] = down;
}
}
void input_process_mouse_move(Input* input, int x, int y) {
input->mouse_dx = x - input->mouse_x;
input->mouse_dy = y - input->mouse_y;
input->mouse_x = x;
input->mouse_y = y;
}
void input_process_mouse_button(Input* input, int button, int down) {
if (button >= 0 && button < 3) {
input->mouse_buttons[button] = down;
}
}
void input_process_mouse_wheel(Input* input, int delta) {
input->mouse_scroll += delta;
}
int input_key_down(Input* input, int key) {
return (key >= 0 && key < MAX_KEYS) ? input->key_states[key] : 0;
}
int input_key_pressed(Input* input, int key) {
return (key >= 0 && key < MAX_KEYS) ? (input->key_states[key] && !input->key_prev[key]) : 0;
}
int input_key_released(Input* input, int key) {
return (key >= 0 && key < MAX_KEYS) ? (!input->key_states[key] && input->key_prev[key]) : 0;
}
int input_mouse_button(Input* input, int button) {
return (button >= 0 && button < 3) ? input->mouse_buttons[button] : 0;
}
int input_mouse_button_pressed(Input* input, int button) {
return (button >= 0 && button < 3) ? (input->mouse_buttons[button] && !input->mouse_prev[button]) : 0;
}
void input_get_mouse_pos(Input* input, int* x, int* y) {
if (x) *x = input->mouse_x;
if (y) *y = input->mouse_y;
}
void input_get_mouse_delta(Input* input, int* dx, int* dy) {
if (dx) *dx = input->mouse_dx;
if (dy) *dy = input->mouse_dy;
}
int input_get_mouse_scroll(Input* input) {
return input->mouse_scroll;
}
int input_gamepad_connected(Input* input) {
return input->gamepad_connected;
}
float input_gamepad_axis_lx(Input* input) {
float v = normalize_axis(input->gamepad_state.Gamepad.sThumbLX);
return fabsf(v) < input->deadzone ? 0.0f : v;
}
float input_gamepad_axis_ly(Input* input) {
float v = normalize_axis(input->gamepad_state.Gamepad.sThumbLY);
return fabsf(v) < input->deadzone ? 0.0f : v;
}
float input_gamepad_axis_rx(Input* input) {
float v = normalize_axis(input->gamepad_state.Gamepad.sThumbRX);
return fabsf(v) < input->deadzone ? 0.0f : v;
}
float input_gamepad_axis_ry(Input* input) {
float v = normalize_axis(input->gamepad_state.Gamepad.sThumbRY);
return fabsf(v) < input->deadzone ? 0.0f : v;
}
float input_gamepad_trigger_l(Input* input) {
return normalize_trigger(input->gamepad_state.Gamepad.bLeftTrigger);
}
float input_gamepad_trigger_r(Input* input) {
return normalize_trigger(input->gamepad_state.Gamepad.bRightTrigger);
}
int input_gamepad_button(Input* input, uint16_t button) {
return (input->gamepad_state.Gamepad.wButtons & button) != 0;
}
int input_gamepad_button_pressed(Input* input, uint16_t button) {
return (input->gamepad_state.Gamepad.wButtons & button) != 0 &&
(input->gamepad_prev.Gamepad.wButtons & button) == 0;
}
+88
View File
@@ -0,0 +1,88 @@
#pragma once
// ============================================================================
// input.h - 输入管理 (键盘/鼠标/手柄)
// ============================================================================
#include <Windows.h>
#include <Xinput.h>
#include <stdint.h>
#pragma comment(lib, "xinput.lib")
// 键盘状态
#define MAX_KEYS 256
// 鼠标按钮
#define MOUSE_LEFT 0
#define MOUSE_RIGHT 1
#define MOUSE_MIDDLE 2
// 手柄按钮
#define GAMEPAD_A XINPUT_GAMEPAD_A
#define GAMEPAD_B XINPUT_GAMEPAD_B
#define GAMEPAD_X XINPUT_GAMEPAD_X
#define GAMEPAD_Y XINPUT_GAMEPAD_Y
#define GAMEPAD_START XINPUT_GAMEPAD_START
#define GAMEPAD_BACK XINPUT_GAMEPAD_BACK
#define GAMEPAD_LB XINPUT_GAMEPAD_LEFT_SHOULDER
#define GAMEPAD_RB XINPUT_GAMEPAD_RIGHT_SHOULDER
#define GAMEPAD_LTHUMB XINPUT_GAMEPAD_LEFT_THUMB
#define GAMEPAD_RTHUMB XINPUT_GAMEPAD_RIGHT_THUMB
#define GAMEPAD_UP XINPUT_GAMEPAD_DPAD_UP
#define GAMEPAD_DOWN XINPUT_GAMEPAD_DPAD_DOWN
#define GAMEPAD_LEFT XINPUT_GAMEPAD_DPAD_LEFT
#define GAMEPAD_RIGHT XINPUT_GAMEPAD_DPAD_RIGHT
typedef struct {
int key_states[MAX_KEYS];
int key_prev[MAX_KEYS];
int mouse_x, mouse_y;
int mouse_dx, mouse_dy;
int mouse_scroll;
int mouse_buttons[3];
int mouse_prev[3];
XINPUT_STATE gamepad_state;
XINPUT_STATE gamepad_prev;
int gamepad_connected;
float deadzone;
} Input;
// 创建/销毁
Input* input_create(void);
void input_destroy(Input* input);
// 更新 (每帧调用)
void input_update(Input* input);
// Win32 消息处理
void input_process_key(Input* input, int key, int down);
void input_process_mouse_move(Input* input, int x, int y);
void input_process_mouse_button(Input* input, int button, int down);
void input_process_mouse_wheel(Input* input, int delta);
// 键盘查询
int input_key_down(Input* input, int key);
int input_key_pressed(Input* input, int key);
int input_key_released(Input* input, int key);
// 鼠标查询
int input_mouse_button(Input* input, int button);
int input_mouse_button_pressed(Input* input, int button);
void input_get_mouse_pos(Input* input, int* x, int* y);
void input_get_mouse_delta(Input* input, int* dx, int* dy);
int input_get_mouse_scroll(Input* input);
// 手柄查询
int input_gamepad_connected(Input* input);
float input_gamepad_axis_lx(Input* input);
float input_gamepad_axis_ly(Input* input);
float input_gamepad_axis_rx(Input* input);
float input_gamepad_axis_ry(Input* input);
float input_gamepad_trigger_l(Input* input);
float input_gamepad_trigger_r(Input* input);
int input_gamepad_button(Input* input, uint16_t button);
int input_gamepad_button_pressed(Input* input, uint16_t button);
+8
View File
@@ -0,0 +1,8 @@
// math_impl.c - 数学函数实现 (C fallback)
// 当 MASM 不可用时使用
#include "math_c_fallback.h"
// 由于函数是 static inline,它们应该被内联
// 但如果编译器没有内联,这些函数将提供实现
// 这个文件确保链接器能找到符号
+373
View File
@@ -0,0 +1,373 @@
#include "physics.h"
#include "../include/engine_api.h"
#include <stdlib.h>
#include <math.h>
#include <float.h>
static void move_collider(Collider* c, Vec3 delta);
PhysicsWorld* physics_create(void) {
PhysicsWorld* w = (PhysicsWorld*)calloc(1, sizeof(PhysicsWorld));
w->gravity_enabled = 1;
w->gravity = (Vec3){0, -9.81f, 0};
return w;
}
void physics_destroy(PhysicsWorld* world) {
free(world);
}
static void init_body(RigidBody* b) {
b->velocity = (Vec3){0, 0, 0};
b->acceleration = (Vec3){0, 0, 0};
b->mass = 1.0f;
b->restitution = 0.5f;
b->friction = 0.3f;
b->is_kinematic = 0;
}
uint32_t physics_add_aabb(PhysicsWorld* world, uint32_t entity, Vec3 min, Vec3 max) {
uint32_t idx = world->collider_count++;
Collider* c = &world->colliders[idx];
c->entity_id = entity;
c->type = COLLIDER_AABB;
c->aabb.min = min;
c->aabb.max = max;
c->is_trigger = 0;
c->is_static = 0;
init_body(&c->body);
return idx;
}
uint32_t physics_add_sphere(PhysicsWorld* world, uint32_t entity, Vec3 center, float radius) {
uint32_t idx = world->collider_count++;
Collider* c = &world->colliders[idx];
c->entity_id = entity;
c->type = COLLIDER_SPHERE;
c->sphere.center = center;
c->sphere.radius = radius;
c->is_trigger = 0;
c->is_static = 0;
init_body(&c->body);
return idx;
}
uint32_t physics_add_plane(PhysicsWorld* world, uint32_t entity, Vec3 normal, float distance) {
uint32_t idx = world->collider_count++;
Collider* c = &world->colliders[idx];
c->entity_id = entity;
c->type = COLLIDER_PLANE;
vec3_normalize(&normal, &c->plane.normal);
c->plane.distance = distance;
c->is_trigger = 0;
c->is_static = 1;
init_body(&c->body);
c->body.mass = 0.0f;
c->body.is_kinematic = 1;
return idx;
}
void physics_remove_collider(PhysicsWorld* world, uint32_t index) {
if (index < world->collider_count) {
world->colliders[index] = world->colliders[--world->collider_count];
}
}
void physics_update_collider(PhysicsWorld* world, uint32_t index, Vec3 position) {
if (!world || index >= world->collider_count) return;
Collider* c = &world->colliders[index];
if (c->type == COLLIDER_SPHERE) {
c->sphere.center = position;
} else if (c->type == COLLIDER_AABB) {
Vec3 center = {
(c->aabb.min.x + c->aabb.max.x) * 0.5f,
(c->aabb.min.y + c->aabb.max.y) * 0.5f,
(c->aabb.min.z + c->aabb.max.z) * 0.5f
};
Vec3 delta;
vec3_sub(&position, &center, &delta);
move_collider(c, delta);
}
}
static int aabb_vs_aabb(const Collider* a, const Collider* b, Vec3* normal, float* depth) {
Vec3 diff;
Vec3 centerA = {(a->aabb.min.x + a->aabb.max.x) * 0.5f, (a->aabb.min.y + a->aabb.max.y) * 0.5f, (a->aabb.min.z + a->aabb.max.z) * 0.5f};
Vec3 centerB = {(b->aabb.min.x + b->aabb.max.x) * 0.5f, (b->aabb.min.y + b->aabb.max.y) * 0.5f, (b->aabb.min.z + b->aabb.max.z) * 0.5f};
vec3_sub(&centerA, &centerB, &diff);
Vec3 half_a, half_b;
vec3_sub(&a->aabb.max, &a->aabb.min, &half_a);
vec3_scale(&half_a, 0.5f, &half_a);
vec3_sub(&b->aabb.max, &b->aabb.min, &half_b);
vec3_scale(&half_b, 0.5f, &half_b);
float dx = fabsf(diff.x) - (half_a.x + half_b.x);
float dy = fabsf(diff.y) - (half_a.y + half_b.y);
float dz = fabsf(diff.z) - (half_a.z + half_b.z);
if (dx < 0 && dy < 0 && dz < 0) {
*depth = -fmaxf(fmaxf(dx, dy), dz);
if (dx > dy && dx > dz) { *normal = (Vec3){copysignf(1.0f, diff.x), 0, 0}; }
else if (dy > dz) { normal->x = 0; normal->y = copysignf(1.0f, diff.y); normal->z = 0; }
else { normal->x = 0; normal->y = 0; normal->z = copysignf(1.0f, diff.z); }
return 1;
}
return 0;
}
static int sphere_vs_sphere(const Collider* a, const Collider* b, Vec3* normal, float* depth) {
Vec3 diff;
vec3_sub(&a->sphere.center, &b->sphere.center, &diff);
float dist = vec3_length(&diff);
float radii = a->sphere.radius + b->sphere.radius;
if (dist < radii) {
*depth = radii - dist;
if (dist > 0.0001f) {
vec3_normalize(&diff, normal);
} else {
*normal = (Vec3){0, 1, 0};
}
return 1;
}
return 0;
}
static int sphere_vs_aabb(const Collider* sphere, const Collider* aabb, Vec3* normal, float* depth) {
Vec3 closest;
closest.x = fmaxf(aabb->aabb.min.x, fminf(sphere->sphere.center.x, aabb->aabb.max.x));
closest.y = fmaxf(aabb->aabb.min.y, fminf(sphere->sphere.center.y, aabb->aabb.max.y));
closest.z = fmaxf(aabb->aabb.min.z, fminf(sphere->sphere.center.z, aabb->aabb.max.z));
Vec3 diff;
vec3_sub(&sphere->sphere.center, &closest, &diff);
float dist_sq = vec3_dot(&diff, &diff);
if (dist_sq < sphere->sphere.radius * sphere->sphere.radius) {
float dist = sqrtf(dist_sq);
*depth = sphere->sphere.radius - dist;
if (dist > 0.0001f) {
vec3_normalize(&diff, normal);
} else {
*normal = (Vec3){0, 1, 0};
}
return 1;
}
return 0;
}
static int sphere_vs_plane(const Collider* sphere, const Collider* plane, Vec3* normal, float* depth) {
float dist = vec3_dot(&sphere->sphere.center, &plane->plane.normal) - plane->plane.distance;
if (dist < sphere->sphere.radius) {
*depth = sphere->sphere.radius - dist;
*normal = plane->plane.normal;
return 1;
}
return 0;
}
int physics_check_collision(const Collider* a, const Collider* b, Vec3* normal, float* depth) {
if (a->type == COLLIDER_AABB && b->type == COLLIDER_AABB)
return aabb_vs_aabb(a, b, normal, depth);
if (a->type == COLLIDER_SPHERE && b->type == COLLIDER_SPHERE)
return sphere_vs_sphere(a, b, normal, depth);
if (a->type == COLLIDER_SPHERE && b->type == COLLIDER_AABB)
return sphere_vs_aabb(a, b, normal, depth);
if (a->type == COLLIDER_AABB && b->type == COLLIDER_SPHERE) {
int hit = sphere_vs_aabb(b, a, normal, depth);
if (hit) { normal->x = -normal->x; normal->y = -normal->y; normal->z = -normal->z; }
return hit;
}
if (a->type == COLLIDER_SPHERE && b->type == COLLIDER_PLANE)
return sphere_vs_plane(a, b, normal, depth);
if (a->type == COLLIDER_PLANE && b->type == COLLIDER_SPHERE) {
int hit = sphere_vs_plane(b, a, normal, depth);
if (hit) { normal->x = -normal->x; normal->y = -normal->y; normal->z = -normal->z; }
return hit;
}
return 0;
}
int physics_raycast(PhysicsWorld* world, Vec3 origin, Vec3 direction, float max_dist,
uint32_t* hit_entity, Vec3* hit_point, Vec3* hit_normal) {
vec3_normalize(&direction, &direction);
float closest_t = max_dist;
int hit = 0;
for (uint32_t i = 0; i < world->collider_count; i++) {
Collider* c = &world->colliders[i];
if (c->type == COLLIDER_SPHERE) {
Vec3 oc;
vec3_sub(&origin, &c->sphere.center, &oc);
float a = vec3_dot(&direction, &direction);
float b = 2.0f * vec3_dot(&oc, &direction);
float cc = vec3_dot(&oc, &oc) - c->sphere.radius * c->sphere.radius;
float disc = b * b - 4 * a * cc;
if (disc >= 0) {
float t = (-b - sqrtf(disc)) / (2 * a);
if (t > 0 && t < closest_t) {
closest_t = t;
*hit_entity = c->entity_id;
Vec3 scaled;
vec3_scale(&direction, t, &scaled);
vec3_add(&origin, &scaled, hit_point);
vec3_sub(hit_point, &c->sphere.center, hit_normal);
vec3_normalize(hit_normal, hit_normal);
hit = 1;
}
}
}
}
return hit;
}
static int body_is_movable(Collider* c) {
return !c->is_static && !c->body.is_kinematic && c->body.mass > 0.0f;
}
static float body_inv_mass(Collider* c) {
if (c->is_static || c->body.is_kinematic || c->body.mass <= 0.0f) return 0.0f;
return 1.0f / c->body.mass;
}
static void move_collider(Collider* c, Vec3 delta) {
if (c->type == COLLIDER_SPHERE) {
vec3_add(&c->sphere.center, &delta, &c->sphere.center);
} else if (c->type == COLLIDER_AABB) {
vec3_add(&c->aabb.min, &delta, &c->aabb.min);
vec3_add(&c->aabb.max, &delta, &c->aabb.max);
}
}
RigidBody* physics_get_body(PhysicsWorld* world, uint32_t index) {
if (!world || index >= world->collider_count) return NULL;
return &world->colliders[index].body;
}
void physics_apply_force(PhysicsWorld* world, uint32_t index, Vec3 force) {
RigidBody* b = physics_get_body(world, index);
if (!b || b->mass <= 0.0f) return;
Vec3 scaled;
vec3_scale(&force, 1.0f / b->mass, &scaled);
vec3_add(&b->acceleration, &scaled, &b->acceleration);
}
void physics_apply_impulse(PhysicsWorld* world, uint32_t index, Vec3 impulse) {
RigidBody* b = physics_get_body(world, index);
if (!b || b->mass <= 0.0f) return;
Vec3 scaled;
vec3_scale(&impulse, 1.0f / b->mass, &scaled);
vec3_add(&b->velocity, &scaled, &b->velocity);
}
void physics_step(PhysicsWorld* world, float dt) {
if (!world || world->collider_count == 0 || dt <= 0.0f) return;
const float linear_damping = 0.995f;
// 1. 积分动态刚体
for (uint32_t i = 0; i < world->collider_count; i++) {
Collider* c = &world->colliders[i];
if (!body_is_movable(c)) continue;
RigidBody* b = &c->body;
if (world->gravity_enabled) {
vec3_add(&b->acceleration, &world->gravity, &b->acceleration);
}
Vec3 dv;
vec3_scale(&b->acceleration, dt, &dv);
vec3_add(&b->velocity, &dv, &b->velocity);
b->acceleration = (Vec3){0, 0, 0};
vec3_scale(&b->velocity, linear_damping, &b->velocity);
Vec3 dp;
vec3_scale(&b->velocity, dt, &dp);
move_collider(c, dp);
}
// 2. 碰撞检测与响应
for (uint32_t i = 0; i < world->collider_count; i++) {
Collider* a = &world->colliders[i];
for (uint32_t j = i + 1; j < world->collider_count; j++) {
Collider* b = &world->colliders[j];
if (a->is_static && b->is_static) continue;
Vec3 normal;
float depth = 0.0f;
if (!physics_check_collision(a, b, &normal, &depth)) continue;
// 触发器只报告,不解决
if (a->is_trigger || b->is_trigger) continue;
float inv_a = body_inv_mass(a);
float inv_b = body_inv_mass(b);
float total_inv = inv_a + inv_b;
if (total_inv <= 0.0f) continue;
// 位置修正(防止沉入)
float percent = 0.5f; // 修正比例
Vec3 correction;
vec3_scale(&normal, depth * percent / total_inv, &correction);
if (inv_a > 0.0f) {
Vec3 da; vec3_scale(&correction, inv_a, &da);
da.x = -da.x; da.y = -da.y; da.z = -da.z;
move_collider(a, da);
}
if (inv_b > 0.0f) {
Vec3 db; vec3_scale(&correction, inv_b, &db);
move_collider(b, db);
}
// 冲量响应
Vec3 rel_v;
vec3_sub(&a->body.velocity, &b->body.velocity, &rel_v);
float vel_along_normal = vec3_dot(&rel_v, &normal);
if (vel_along_normal > 0.0f) continue; // 正在分离
float e = fminf(a->body.restitution, b->body.restitution);
float j = -(1.0f + e) * vel_along_normal / total_inv;
Vec3 impulse;
vec3_scale(&normal, j, &impulse);
if (inv_a > 0.0f) {
Vec3 va; vec3_scale(&impulse, inv_a, &va);
vec3_add(&a->body.velocity, &va, &a->body.velocity);
}
if (inv_b > 0.0f) {
Vec3 vb; vec3_scale(&impulse, inv_b, &vb);
vec3_sub(&b->body.velocity, &vb, &b->body.velocity);
}
// 摩擦力
vec3_sub(&a->body.velocity, &b->body.velocity, &rel_v);
Vec3 tangent;
Vec3 vn; vec3_scale(&normal, vec3_dot(&rel_v, &normal), &vn);
vec3_sub(&rel_v, &vn, &tangent);
float tan_len = vec3_length(&tangent);
if (tan_len > 0.0001f) {
vec3_scale(&tangent, 1.0f / tan_len, &tangent);
float jt = -vec3_dot(&rel_v, &tangent) / total_inv;
float mu = sqrtf(a->body.friction * a->body.friction + b->body.friction * b->body.friction);
if (jt > j * mu) jt = j * mu;
if (jt < -j * mu) jt = -j * mu;
Vec3 friction_impulse;
vec3_scale(&tangent, jt, &friction_impulse);
if (inv_a > 0.0f) {
Vec3 va; vec3_scale(&friction_impulse, inv_a, &va);
vec3_add(&a->body.velocity, &va, &a->body.velocity);
}
if (inv_b > 0.0f) {
Vec3 vb; vec3_scale(&friction_impulse, inv_b, &vb);
vec3_sub(&b->body.velocity, &vb, &b->body.velocity);
}
}
}
}
}
void physics_set_gravity(PhysicsWorld* world, Vec3 gravity) {
world->gravity = gravity;
}
+74
View File
@@ -0,0 +1,74 @@
#pragma once
// ============================================================================
// physics.h - 物理引擎 (碰撞检测, 调用ASM数学)
// ============================================================================
#include "../include/engine_api.h"
#include "../ASM/math_c_fallback.h"
#include <stdint.h>
#define MAX_COLLIDERS 1024
typedef enum {
COLLIDER_AABB,
COLLIDER_SPHERE,
COLLIDER_PLANE,
} ColliderType;
typedef struct {
Vec3 velocity;
Vec3 acceleration;
float mass;
float restitution;
float friction;
int is_kinematic;
} RigidBody;
typedef struct {
uint32_t entity_id;
ColliderType type;
union {
struct { Vec3 min; Vec3 max; } aabb;
struct { Vec3 center; float radius; } sphere;
struct { Vec3 normal; float distance; } plane;
};
int is_trigger;
int is_static;
RigidBody body;
} Collider;
typedef struct {
Collider colliders[MAX_COLLIDERS];
uint32_t collider_count;
int gravity_enabled;
Vec3 gravity;
} PhysicsWorld;
// 碰撞回调
typedef void (*CollisionCallback)(uint32_t entity_a, uint32_t entity_b, Vec3 normal, float depth);
// 创建/销毁
PhysicsWorld* physics_create(void);
void physics_destroy(PhysicsWorld* world);
// 碰撞体
uint32_t physics_add_aabb(PhysicsWorld* world, uint32_t entity, Vec3 min, Vec3 max);
uint32_t physics_add_sphere(PhysicsWorld* world, uint32_t entity, Vec3 center, float radius);
uint32_t physics_add_plane(PhysicsWorld* world, uint32_t entity, Vec3 normal, float distance);
void physics_remove_collider(PhysicsWorld* world, uint32_t index);
void physics_update_collider(PhysicsWorld* world, uint32_t index, Vec3 position);
// 碰撞检测
int physics_check_collision(const Collider* a, const Collider* b, Vec3* normal, float* depth);
int physics_raycast(PhysicsWorld* world, Vec3 origin, Vec3 direction, float max_dist,
uint32_t* hit_entity, Vec3* hit_point, Vec3* hit_normal);
// 刚体
RigidBody* physics_get_body(PhysicsWorld* world, uint32_t index);
void physics_apply_force(PhysicsWorld* world, uint32_t index, Vec3 force);
void physics_apply_impulse(PhysicsWorld* world, uint32_t index, Vec3 impulse);
// 模拟
void physics_step(PhysicsWorld* world, float dt);
void physics_set_gravity(PhysicsWorld* world, Vec3 gravity);
+135
View File
@@ -0,0 +1,135 @@
#include "camera.h"
#include <math.h>
#include <string.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
Camera camera_perspective(float fov_y, float aspect, float near_z, float far_z) {
Camera cam = {0};
cam.type = CAMERA_PERSPECTIVE;
cam.position = (Vec3){0, 0, -5};
cam.target = (Vec3){0, 0, 0};
cam.up = (Vec3){0, 1, 0};
cam.fov_y = fov_y;
cam.aspect = aspect;
cam.near_z = near_z;
cam.far_z = far_z;
cam.dirty = 1;
return cam;
}
Camera camera_orthographic(float width, float height, float near_z, float far_z) {
Camera cam = {0};
cam.type = CAMERA_ORTHOGRAPHIC;
cam.position = (Vec3){0, 0, -5};
cam.target = (Vec3){0, 0, 0};
cam.up = (Vec3){0, 1, 0};
cam.ortho_width = width;
cam.ortho_height = height;
cam.near_z = near_z;
cam.far_z = far_z;
cam.dirty = 1;
return cam;
}
void camera_update(Camera* camera) {
if (!camera->dirty) return;
mat4_lookat(&camera->position, &camera->target, &camera->up, &camera->view);
if (camera->type == CAMERA_PERSPECTIVE) {
mat4_perspective(camera->fov_y, camera->aspect, camera->near_z, camera->far_z, &camera->projection);
} else {
mat4_orthographic(camera->ortho_width, camera->ortho_height, camera->near_z, camera->far_z, &camera->projection);
}
camera->dirty = 0;
}
const Mat4* camera_get_view(Camera* camera) {
camera_update(camera);
return &camera->view;
}
const Mat4* camera_get_projection(Camera* camera) {
camera_update(camera);
return &camera->projection;
}
void camera_move_forward(Camera* camera, float distance) {
Vec3 dir;
vec3_sub(&camera->target, &camera->position, &dir);
vec3_normalize(&dir, &dir);
Vec3 move;
vec3_scale(&dir, distance, &move);
vec3_add(&camera->position, &move, &camera->position);
vec3_add(&camera->target, &move, &camera->target);
camera->dirty = 1;
}
void camera_move_right(Camera* camera, float distance) {
Vec3 forward, right;
vec3_sub(&camera->target, &camera->position, &forward);
vec3_normalize(&forward, &forward);
vec3_cross(&camera->up, &forward, &right);
vec3_normalize(&right, &right);
Vec3 move;
vec3_scale(&right, distance, &move);
vec3_add(&camera->position, &move, &camera->position);
vec3_add(&camera->target, &move, &camera->target);
camera->dirty = 1;
}
void camera_move_up(Camera* camera, float distance) {
Vec3 move;
vec3_scale(&camera->up, distance, &move);
vec3_add(&camera->position, &move, &camera->position);
vec3_add(&camera->target, &move, &camera->target);
camera->dirty = 1;
}
void camera_rotate_yaw(Camera* camera, float angle) {
Vec3 forward;
vec3_sub(&camera->target, &camera->position, &forward);
float cs = cosf(angle), sn = sinf(angle);
Vec3 right;
vec3_cross(&forward, &camera->up, &right);
vec3_normalize(&right, &right);
Vec3 new_forward = {
forward.x * cs + right.x * sn,
forward.y * cs + right.y * sn,
forward.z * cs + right.z * sn,
};
vec3_add(&camera->position, &new_forward, &camera->target);
camera->dirty = 1;
}
void camera_rotate_pitch(Camera* camera, float angle) {
Vec3 forward;
vec3_sub(&camera->target, &camera->position, &forward);
Vec3 right;
vec3_cross(&forward, &camera->up, &right);
vec3_normalize(&right, &right);
float cs = cosf(angle), sn = sinf(angle);
Vec3 new_forward = {
forward.x * cs + camera->up.x * sn,
forward.y * cs + camera->up.y * sn,
forward.z * cs + camera->up.z * sn,
};
Vec3 new_up;
vec3_cross(&right, &new_forward, &new_up);
camera->up = new_up;
vec3_add(&camera->position, &new_forward, &camera->target);
camera->dirty = 1;
}
void camera_look_at(Camera* camera, Vec3 target) {
camera->target = target;
camera->dirty = 1;
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
// ============================================================================
// camera.h - 摄像机
// ============================================================================
#include "../include/engine_api.h"
#include "../ASM/math_c_fallback.h"
typedef enum {
CAMERA_PERSPECTIVE,
CAMERA_ORTHOGRAPHIC,
} CameraType;
typedef struct {
CameraType type;
Vec3 position;
Vec3 target;
Vec3 up;
float fov_y;
float aspect;
float near_z;
float far_z;
float ortho_width;
float ortho_height;
Mat4 view;
Mat4 projection;
int dirty;
} Camera;
// 创建默认透视摄像机
Camera camera_perspective(float fov_y, float aspect, float near_z, float far_z);
// 创建正交摄像机
Camera camera_orthographic(float width, float height, float near_z, float far_z);
// 更新矩阵
void camera_update(Camera* camera);
// 获取矩阵
const Mat4* camera_get_view(Camera* camera);
const Mat4* camera_get_projection(Camera* camera);
// 移动
void camera_move_forward(Camera* camera, float distance);
void camera_move_right(Camera* camera, float distance);
void camera_move_up(Camera* camera, float distance);
// 旋转
void camera_rotate_yaw(Camera* camera, float angle);
void camera_rotate_pitch(Camera* camera, float angle);
// 设置目标
void camera_look_at(Camera* camera, Vec3 target);
@@ -0,0 +1,98 @@
#include "camera_controller.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
CameraController camera_controller_create(Camera* camera) {
CameraController ctrl = {0};
ctrl.camera = camera;
ctrl.mode = CAMERA_MODE_FLY;
ctrl.move_speed = 10.0f;
ctrl.mouse_sensitivity = 0.003f;
ctrl.height_offset = 1.75f;
ctrl.pitch_min = -1.45f;
ctrl.pitch_max = 1.45f;
ctrl.yaw = 0.0f;
ctrl.pitch = 0.0f;
ctrl.ground_height_fn = NULL;
ctrl.ground_user = NULL;
return ctrl;
}
void camera_controller_set_ground_callback(CameraController* ctrl,
float (*get_height)(float x, float z, void* user),
void* user) {
ctrl->ground_height_fn = get_height;
ctrl->ground_user = user;
}
void camera_controller_set_mode(CameraController* ctrl, CameraMode mode) {
ctrl->mode = mode;
}
void camera_controller_look(CameraController* ctrl, float delta_x, float delta_y) {
ctrl->yaw -= delta_x * ctrl->mouse_sensitivity;
ctrl->pitch -= delta_y * ctrl->mouse_sensitivity;
if (ctrl->pitch < ctrl->pitch_min) ctrl->pitch = ctrl->pitch_min;
if (ctrl->pitch > ctrl->pitch_max) ctrl->pitch = ctrl->pitch_max;
// 重建 target
Vec3 forward;
forward.x = cosf(ctrl->pitch) * sinf(ctrl->yaw);
forward.y = sinf(ctrl->pitch);
forward.z = cosf(ctrl->pitch) * cosf(ctrl->yaw);
vec3_normalize(&forward, &forward);
vec3_add(&ctrl->camera->position, &forward, &ctrl->camera->target);
ctrl->camera->dirty = 1;
}
void camera_controller_move(CameraController* ctrl, Vec3 dir, float dt) {
Camera* cam = ctrl->camera;
Vec3 forward, right, up;
vec3_sub(&cam->target, &cam->position, &forward);
forward.y = 0.0f;
vec3_normalize(&forward, &forward);
vec3_cross(&cam->up, &forward, &right);
vec3_normalize(&right, &right);
up = cam->up;
Vec3 move = {0};
Vec3 tmp;
vec3_scale(&right, dir.x, &tmp);
vec3_add(&move, &tmp, &move);
vec3_scale(&up, dir.y, &tmp);
vec3_add(&move, &tmp, &move);
vec3_scale(&forward, dir.z, &tmp);
vec3_add(&move, &tmp, &move);
float len = vec3_length(&move);
if (len > 0.0001f) {
vec3_scale(&move, ctrl->move_speed * dt / len, &move);
vec3_add(&cam->position, &move, &cam->position);
vec3_add(&cam->target, &move, &cam->target);
cam->dirty = 1;
}
}
void camera_controller_update(CameraController* ctrl, float dt) {
(void)dt;
if (ctrl->mode != CAMERA_MODE_WALK) return;
if (!ctrl->ground_height_fn) return;
float ground = ctrl->ground_height_fn(ctrl->camera->position.x, ctrl->camera->position.z, ctrl->ground_user);
float desired_y = ground + ctrl->height_offset;
float delta = desired_y - ctrl->camera->position.y;
if (fabsf(delta) > 0.01f) {
Vec3 move = {0, delta, 0};
vec3_add(&ctrl->camera->position, &move, &ctrl->camera->position);
vec3_add(&ctrl->camera->target, &move, &ctrl->camera->target);
ctrl->camera->dirty = 1;
}
}
@@ -0,0 +1,44 @@
#pragma once
// ============================================================================
// camera_controller.h - 增强相机控制器(飞行 / 步行模式)
// ============================================================================
#include "camera.h"
#include "../ASM/math_c_fallback.h"
typedef enum {
CAMERA_MODE_FLY,
CAMERA_MODE_WALK,
} CameraMode;
typedef struct {
Camera* camera;
CameraMode mode;
float move_speed;
float mouse_sensitivity;
float height_offset; // 步行模式眼睛离地面高度
float pitch_min;
float pitch_max;
float yaw;
float pitch;
float (*ground_height_fn)(float x, float z, void* user);
void* ground_user;
} CameraController;
CameraController camera_controller_create(Camera* camera);
// 更新 yaw/pitch,鼠标输入以 delta 像素为单位
void camera_controller_look(CameraController* ctrl, float delta_x, float delta_y);
// 移动:dir 为本地空间方向 (x=右, y=上, z=前)
void camera_controller_move(CameraController* ctrl, Vec3 dir, float dt);
// 设置地形高度采样回调
void camera_controller_set_ground_callback(CameraController* ctrl, float (*get_height)(float x, float z, void* user), void* user);
// 切换模式
void camera_controller_set_mode(CameraController* ctrl, CameraMode mode);
// 每帧应用约束(如步行贴地)
void camera_controller_update(CameraController* ctrl, float dt);
+60
View File
@@ -0,0 +1,60 @@
#include "frustum.h"
#include <math.h>
void frustum_from_matrix(const Mat4* vp, Vec4 planes[6]) {
// 行主序存储:clip = vec4(pos,1) * vp
// 因此 clip 分量 = dot(pos, vp 的列)
// left = col3 + col0
// right = col3 - col0
// bottom = col3 + col1
// top = col3 - col1
// near = col3 + col2
// far = col3 - col2
planes[0] = (Vec4){ vp->m[0][3] + vp->m[0][0],
vp->m[1][3] + vp->m[1][0],
vp->m[2][3] + vp->m[2][0],
vp->m[3][3] + vp->m[3][0] };
planes[1] = (Vec4){ vp->m[0][3] - vp->m[0][0],
vp->m[1][3] - vp->m[1][0],
vp->m[2][3] - vp->m[2][0],
vp->m[3][3] - vp->m[3][0] };
planes[2] = (Vec4){ vp->m[0][3] + vp->m[0][1],
vp->m[1][3] + vp->m[1][1],
vp->m[2][3] + vp->m[2][1],
vp->m[3][3] + vp->m[3][1] };
planes[3] = (Vec4){ vp->m[0][3] - vp->m[0][1],
vp->m[1][3] - vp->m[1][1],
vp->m[2][3] - vp->m[2][1],
vp->m[3][3] - vp->m[3][1] };
planes[4] = (Vec4){ vp->m[0][3] + vp->m[0][2],
vp->m[1][3] + vp->m[1][2],
vp->m[2][3] + vp->m[2][2],
vp->m[3][3] + vp->m[3][2] };
planes[5] = (Vec4){ vp->m[0][3] - vp->m[0][2],
vp->m[1][3] - vp->m[1][2],
vp->m[2][3] - vp->m[2][2],
vp->m[3][3] - vp->m[3][2] };
for (int i = 0; i < 6; i++) {
float len = sqrtf(planes[i].x * planes[i].x +
planes[i].y * planes[i].y +
planes[i].z * planes[i].z);
if (len > 0.0001f) {
planes[i].x /= len;
planes[i].y /= len;
planes[i].z /= len;
planes[i].w /= len;
}
}
}
int frustum_intersects_sphere(const Vec4 planes[6], Vec3 center, float radius) {
for (int i = 0; i < 6; i++) {
float dist = planes[i].x * center.x +
planes[i].y * center.y +
planes[i].z * center.z +
planes[i].w;
if (dist < -radius) return 0;
}
return 1;
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
#include "../ASM/math_c_fallback.h"
// 从视图*投影矩阵提取 6 个视锥平面(行主序,planes[0..5] = left, right, bottom, top, near, far)
void frustum_from_matrix(const Mat4* vp, Vec4 planes[6]);
// 判断球体是否与视锥相交(1=可见/相交,0=完全在外)
int frustum_intersects_sphere(const Vec4 planes[6], Vec3 center, float radius);
+189
View File
@@ -0,0 +1,189 @@
#include "graphics_device.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
GraphicsDevice* graphics_device_create(void) {
return (GraphicsDevice*)calloc(1, sizeof(GraphicsDevice));
}
void graphics_device_destroy(GraphicsDevice* d) {
if (!d) return;
if (d->depth_stencil) d->depth_stencil->lpVtbl->Release(d->depth_stencil);
if (d->depth_texture) d->depth_texture->lpVtbl->Release(d->depth_texture);
if (d->render_target) d->render_target->lpVtbl->Release(d->render_target);
if (d->swap_chain) d->swap_chain->lpVtbl->Release(d->swap_chain);
if (d->context) d->context->lpVtbl->Release(d->context);
if (d->device) d->device->lpVtbl->Release(d->device);
free(d);
}
static int create_depth_stencil(GraphicsDevice* d, uint32_t w, uint32_t h) {
D3D11_TEXTURE2D_DESC desc = {0};
desc.Width = w; desc.Height = h;
desc.MipLevels = 1; desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
HRESULT hr = d->device->lpVtbl->CreateTexture2D(d->device, &desc, NULL, &d->depth_texture);
if (FAILED(hr)) return 0;
D3D11_DEPTH_STENCIL_VIEW_DESC dsv = {0};
dsv.Format = desc.Format;
dsv.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
hr = d->device->lpVtbl->CreateDepthStencilView(d->device, (ID3D11Resource*)d->depth_texture, &dsv, &d->depth_stencil);
return SUCCEEDED(hr);
}
int graphics_device_init(GraphicsDevice* d, void* hwnd, uint32_t w, uint32_t h, int vsync) {
d->width = w; d->height = h; d->vsync = vsync;
DXGI_SWAP_CHAIN_DESC scd = {0};
scd.BufferCount = 1;
scd.BufferDesc.Width = w;
scd.BufferDesc.Height = h;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferDesc.RefreshRate.Numerator = 60;
scd.BufferDesc.RefreshRate.Denominator = 1;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = (HWND)hwnd;
scd.SampleDesc.Count = 1;
scd.Windowed = TRUE;
scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
D3D_FEATURE_LEVEL levels[] = {D3D_FEATURE_LEVEL_11_0};
UINT flags = 0;
#ifdef _DEBUG
flags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, levels, 1, D3D11_SDK_VERSION, &scd, &d->swap_chain, &d->device, NULL, &d->context);
if (FAILED(hr)) return 0;
ID3D11Texture2D* back_buffer = NULL;
d->swap_chain->lpVtbl->GetBuffer(d->swap_chain, 0, &IID_ID3D11Texture2D, (void**)&back_buffer);
d->device->lpVtbl->CreateRenderTargetView(d->device, (ID3D11Resource*)back_buffer, NULL, &d->render_target);
if (back_buffer) back_buffer->lpVtbl->Release(back_buffer);
create_depth_stencil(d, w, h);
d->context->lpVtbl->OMSetRenderTargets(d->context, 1, &d->render_target, d->depth_stencil);
D3D11_VIEWPORT vp = {0};
vp.Width = (float)w; vp.Height = (float)h;
vp.MaxDepth = 1.0f;
d->context->lpVtbl->RSSetViewports(d->context, 1, &vp);
// 设置默认深度状态 (避免 UILayer init 破坏)
D3D11_DEPTH_STENCIL_DESC dsd = {0};
dsd.DepthEnable = TRUE;
dsd.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsd.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
ID3D11DepthStencilState* default_dss = NULL;
d->device->lpVtbl->CreateDepthStencilState(d->device, &dsd, &default_dss);
if (default_dss) {
d->context->lpVtbl->OMSetDepthStencilState(d->context, default_dss, 0);
default_dss->lpVtbl->Release(default_dss);
}
// 设置默认光栅化状态:背面剔除 + 实体填充
D3D11_RASTERIZER_DESC rd = {0};
rd.FillMode = D3D11_FILL_SOLID;
rd.CullMode = D3D11_CULL_BACK;
rd.FrontCounterClockwise = FALSE;
rd.DepthClipEnable = TRUE;
ID3D11RasterizerState* default_rs = NULL;
d->device->lpVtbl->CreateRasterizerState(d->device, &rd, &default_rs);
if (default_rs) {
d->context->lpVtbl->RSSetState(d->context, default_rs);
default_rs->lpVtbl->Release(default_rs);
}
return 1;
}
void graphics_device_resize(GraphicsDevice* d, uint32_t w, uint32_t h) {
if (w == 0 || h == 0) return;
d->context->lpVtbl->OMSetRenderTargets(d->context, 0, NULL, NULL);
if (d->render_target) { d->render_target->lpVtbl->Release(d->render_target); d->render_target = NULL; }
if (d->depth_stencil) { d->depth_stencil->lpVtbl->Release(d->depth_stencil); d->depth_stencil = NULL; }
if (d->depth_texture) { d->depth_texture->lpVtbl->Release(d->depth_texture); d->depth_texture = NULL; }
d->swap_chain->lpVtbl->ResizeBuffers(d->swap_chain, 0, w, h, DXGI_FORMAT_UNKNOWN, 0);
ID3D11Texture2D* back_buffer = NULL;
d->swap_chain->lpVtbl->GetBuffer(d->swap_chain, 0, &IID_ID3D11Texture2D, (void**)&back_buffer);
d->device->lpVtbl->CreateRenderTargetView(d->device, (ID3D11Resource*)back_buffer, NULL, &d->render_target);
if (back_buffer) back_buffer->lpVtbl->Release(back_buffer);
create_depth_stencil(d, w, h);
d->context->lpVtbl->OMSetRenderTargets(d->context, 1, &d->render_target, d->depth_stencil);
D3D11_VIEWPORT vp = {0};
vp.Width = (float)w; vp.Height = (float)h; vp.MaxDepth = 1.0f;
d->context->lpVtbl->RSSetViewports(d->context, 1, &vp);
d->width = w; d->height = h;
}
void graphics_device_clear(GraphicsDevice* d, float r, float g, float b, float a) {
float color[4] = {r, g, b, a};
d->context->lpVtbl->ClearRenderTargetView(d->context, d->render_target, color);
d->context->lpVtbl->ClearDepthStencilView(d->context, d->depth_stencil, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
}
void graphics_device_present(GraphicsDevice* d) {
d->swap_chain->lpVtbl->Present(d->swap_chain, d->vsync ? 1 : 0, 0);
}
int graphics_device_save_screenshot(GraphicsDevice* d, const char* path) {
if (!d || !d->swap_chain || !d->device || !d->context) return 0;
ID3D11Texture2D* back = NULL;
HRESULT hr = d->swap_chain->lpVtbl->GetBuffer(d->swap_chain, 0, &IID_ID3D11Texture2D, (void**)&back);
if (FAILED(hr) || !back) return 0;
D3D11_TEXTURE2D_DESC desc = {0};
back->lpVtbl->GetDesc(back, &desc);
D3D11_TEXTURE2D_DESC sd = desc;
sd.BindFlags = 0;
sd.Usage = D3D11_USAGE_STAGING;
sd.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
sd.MiscFlags = 0;
ID3D11Texture2D* staging = NULL;
hr = d->device->lpVtbl->CreateTexture2D(d->device, &sd, NULL, &staging);
if (FAILED(hr) || !staging) { back->lpVtbl->Release(back); return 0; }
d->context->lpVtbl->CopyResource(d->context, (ID3D11Resource*)staging, (ID3D11Resource*)back);
D3D11_MAPPED_SUBRESOURCE map = {0};
hr = d->context->lpVtbl->Map(d->context, (ID3D11Resource*)staging, 0, D3D11_MAP_READ, 0, &map);
if (FAILED(hr)) { staging->lpVtbl->Release(staging); back->lpVtbl->Release(back); return 0; }
FILE* f = fopen(path, "wb");
if (!f) { d->context->lpVtbl->Unmap(d->context, (ID3D11Resource*)staging, 0); staging->lpVtbl->Release(staging); back->lpVtbl->Release(back); return 0; }
int w = (int)desc.Width;
int h = (int)desc.Height;
int row_size = ((w * 3 + 3) / 4) * 4;
int file_size = 54 + row_size * h;
unsigned char header[54] = {0};
header[0] = 'B'; header[1] = 'M';
memcpy(header + 2, &file_size, 4);
header[10] = 54;
header[14] = 40;
memcpy(header + 18, &w, 4);
memcpy(header + 22, &h, 4);
header[26] = 1;
header[28] = 24;
fwrite(header, 1, 54, f);
unsigned char* row = (unsigned char*)malloc(row_size);
for (int y = 0; y < h; y++) {
uint8_t* src = (uint8_t*)map.pData + (h - 1 - y) * map.RowPitch;
for (int x = 0; x < w; x++) {
// 后台缓冲区为 RGBA,BMP 24-bit 需要 BGR
row[x * 3 + 0] = src[x * 4 + 2];
row[x * 3 + 1] = src[x * 4 + 1];
row[x * 3 + 2] = src[x * 4 + 0];
}
fwrite(row, 1, row_size, f);
}
free(row);
fclose(f);
d->context->lpVtbl->Unmap(d->context, (ID3D11Resource*)staging, 0);
staging->lpVtbl->Release(staging);
back->lpVtbl->Release(back);
return 1;
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <d3d11.h>
#include <dxgi.h>
#include <stdint.h>
typedef struct GraphicsDevice GraphicsDevice;
struct GraphicsDevice {
ID3D11Device* device;
ID3D11DeviceContext* context;
IDXGISwapChain* swap_chain;
ID3D11RenderTargetView* render_target;
ID3D11DepthStencilView* depth_stencil;
ID3D11Texture2D* depth_texture;
uint32_t width;
uint32_t height;
int vsync;
};
GraphicsDevice* graphics_device_create(void);
void graphics_device_destroy(GraphicsDevice* d);
int graphics_device_init(GraphicsDevice* d, void* hwnd, uint32_t w, uint32_t h, int vsync);
void graphics_device_resize(GraphicsDevice* d, uint32_t w, uint32_t h);
void graphics_device_clear(GraphicsDevice* d, float r, float g, float b, float a);
void graphics_device_present(GraphicsDevice* d);
int graphics_device_save_screenshot(GraphicsDevice* d, const char* path);
+26
View File
@@ -0,0 +1,26 @@
#include "lighting.h"
#include <stdlib.h>
#include <string.h>
Light* light_create_dir(float r, float g, float b, float intensity, float dx, float dy, float dz) {
Light* l = (Light*)calloc(1, sizeof(Light));
l->type = LIGHT_DIR;
l->color[0] = r; l->color[1] = g; l->color[2] = b;
l->intensity = intensity;
l->direction[0] = dx; l->direction[1] = dy; l->direction[2] = dz;
float len = sqrtf(dx*dx + dy*dy + dz*dz);
if (len > 0.0001f) { l->direction[0] /= len; l->direction[1] /= len; l->direction[2] /= len; }
return l;
}
Light* light_create_point(float r, float g, float b, float intensity, float x, float y, float z, float range) {
Light* l = (Light*)calloc(1, sizeof(Light));
l->type = LIGHT_POINT;
l->color[0] = r; l->color[1] = g; l->color[2] = b;
l->intensity = intensity;
l->position[0] = x; l->position[1] = y; l->position[2] = z;
l->range = range;
return l;
}
void light_destroy(Light* light) { free(light); }
+17
View File
@@ -0,0 +1,17 @@
// lighting.h
#pragma once
typedef enum { LIGHT_DIR=0, LIGHT_POINT } LightType;
typedef struct {
LightType type;
float color[3];
float intensity;
float direction[3];
float position[3];
float range;
} Light;
Light* light_create_dir(float r, float g, float b, float intensity, float dx, float dy, float dz);
Light* light_create_point(float r, float g, float b, float intensity, float x, float y, float z, float range);
void light_destroy(Light* light);
+15
View File
@@ -0,0 +1,15 @@
#include "material.h"
#include <stdlib.h>
Material* material_create(Shader* shader, Texture* texture) {
Material* m = (Material*)calloc(1, sizeof(Material));
m->shader = shader;
m->texture = texture;
m->color = (Vec4){1.0f, 1.0f, 1.0f, 1.0f};
m->use_texture = (texture != NULL) ? 1 : 0;
return m;
}
void material_destroy(Material* m) {
free(m);
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "shader.h"
#include "texture.h"
#include "../ASM/math_c_fallback.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
Shader* shader;
Texture* texture;
Vec4 color;
int use_texture;
} Material;
Material* material_create(Shader* shader, Texture* texture);
void material_destroy(Material* m);
#ifdef __cplusplus
}
#endif
+204
View File
@@ -0,0 +1,204 @@
#include "mesh.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#ifndef MATH_ASM_AVAILABLE
#include "../ASM/math_c_fallback.h"
#endif
#define POS(n,x,y,z,nx,ny,nz,u,v,r,g,b,a) {x,y,z, nx,ny,nz, u,v, r,g,b,a}
static Mesh* create_mesh(ID3D11Device* dev, const void* verts, uint32_t vc, const uint32_t* idx, uint32_t ic, uint32_t stride) {
Mesh* m = (Mesh*)calloc(1, sizeof(Mesh));
m->stride = stride; m->vcount = vc; m->icount = ic;
D3D11_BUFFER_DESC bd = {0};
D3D11_SUBRESOURCE_DATA sd = {0};
bd.ByteWidth = stride * vc; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = verts;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->vb);
if (idx && ic > 0) {
bd.ByteWidth = sizeof(uint32_t) * ic; bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = idx;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->ib);
}
return m;
}
void mesh_destroy(Mesh* m) {
if (!m) return;
if (m->ib) m->ib->lpVtbl->Release(m->ib);
if (m->vb) m->vb->lpVtbl->Release(m->vb);
free(m);
}
void mesh_draw(Mesh* m, ID3D11DeviceContext* ctx) {
UINT stride = m->stride, offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &m->vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, m->ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->DrawIndexed(ctx, m->icount, 0, 0);
}
Mesh* mesh_create_cube(ID3D11Device* dev) {
typedef struct { float p[3], n[3], uv[2], c[4]; } V;
V v[24] = {
POS(0,-.5f,-.5f,-.5f, 0,0,-1, 0,1, 1,1,1,1), POS(1, .5f,-.5f,-.5f, 0,0,-1, 1,1, 1,1,1,1),
POS(2, .5f, .5f,-.5f, 0,0,-1, 1,0, 1,1,1,1), POS(3,-.5f, .5f,-.5f, 0,0,-1, 0,0, 1,1,1,1),
POS(4, .5f,-.5f, .5f, 0,0, 1, 0,1, 1,1,1,1), POS(5,-.5f,-.5f, .5f, 0,0, 1, 1,1, 1,1,1,1),
POS(6,-.5f, .5f, .5f, 0,0, 1, 1,0, 1,1,1,1), POS(7, .5f, .5f, .5f, 0,0, 1, 0,0, 1,1,1,1),
POS(8,-.5f, .5f,-.5f, 0,1, 0, 0,1, 1,1,1,1), POS(9, .5f, .5f,-.5f, 0,1, 0, 1,1, 1,1,1,1),
POS(10,.5f, .5f, .5f, 0,1, 0, 1,0, 1,1,1,1), POS(11,-.5f, .5f, .5f, 0,1, 0, 0,0, 1,1,1,1),
POS(12,-.5f,-.5f, .5f, 0,-1,0, 0,1, 1,1,1,1), POS(13,.5f,-.5f, .5f, 0,-1,0, 1,1, 1,1,1,1),
POS(14,.5f,-.5f,-.5f, 0,-1,0, 1,0, 1,1,1,1), POS(15,-.5f,-.5f,-.5f, 0,-1,0, 0,0, 1,1,1,1),
POS(16,.5f,-.5f,-.5f, 1,0, 0, 0,1, 1,1,1,1), POS(17,.5f,-.5f, .5f, 1,0, 0, 1,1, 1,1,1,1),
POS(18,.5f, .5f, .5f, 1,0, 0, 1,0, 1,1,1,1), POS(19,.5f, .5f,-.5f, 1,0, 0, 0,0, 1,1,1,1),
POS(20,-.5f,-.5f, .5f, -1,0,0, 0,1, 1,1,1,1), POS(21,-.5f,-.5f,-.5f, -1,0,0, 1,1, 1,1,1,1),
POS(22,-.5f, .5f,-.5f, -1,0,0, 1,0, 1,1,1,1), POS(23,-.5f, .5f, .5f, -1,0,0, 0,0, 1,1,1,1),
};
uint32_t idx[36];
for (int i = 0; i < 6; i++) { int b = i*4; idx[i*6]=b; idx[i*6+1]=b+1; idx[i*6+2]=b+2; idx[i*6+3]=b; idx[i*6+4]=b+2; idx[i*6+5]=b+3; }
return create_mesh(dev, v, 24, idx, 36, sizeof(V));
}
Mesh* mesh_create_plane(ID3D11Device* dev, float size) {
typedef struct { float p[3], n[3], uv[2], c[4]; } V;
float h = size * 0.5f;
V v[4] = {
{{-h,0,h},{0,1,0},{0,1},{1,1,1,1}},
{{ h,0,h},{0,1,0},{1,1},{1,1,1,1}},
{{ h,0,-h},{0,1,0},{1,0},{1,1,1,1}},
{{-h,0,-h},{0,1,0},{0,0},{1,1,1,1}},
};
uint32_t idx[6] = {0,1,2,0,2,3};
return create_mesh(dev, v, 4, idx, 6, sizeof(V));
}
Mesh* mesh_create_sphere(ID3D11Device* dev, float r, int seg) {
typedef struct { float p[3], n[3], uv[2], c[4]; } V;
int vc = (seg+1)*(seg+1);
V* v = (V*)malloc(vc * sizeof(V));
int vi = 0;
for (int lat = 0; lat <= seg; lat++) {
float theta = 3.14159265f * lat / seg;
float st = sinf(theta), ct = cosf(theta);
for (int lon = 0; lon <= seg; lon++) {
float phi = 2.0f * 3.14159265f * lon / seg;
float sp = sinf(phi), cp = cosf(phi);
float nx = cp*st, ny = ct, nz = sp*st;
v[vi].p[0] = nx*r; v[vi].p[1] = ny*r; v[vi].p[2] = nz*r;
v[vi].n[0] = nx; v[vi].n[1] = ny; v[vi].n[2] = nz;
v[vi].uv[0] = (float)lon/seg; v[vi].uv[1] = (float)lat/seg;
v[vi].c[0]=1; v[vi].c[1]=1; v[vi].c[2]=1; v[vi].c[3]=1;
vi++;
}
}
int ic = seg*seg*6;
uint32_t* idx = (uint32_t*)malloc(ic * sizeof(uint32_t));
int ii = 0;
for (int lat = 0; lat < seg; lat++) {
for (int lon = 0; lon < seg; lon++) {
uint32_t f = lat*(seg+1)+lon, s = f+seg+1;
idx[ii++]=f; idx[ii++]=s; idx[ii++]=f+1;
idx[ii++]=s; idx[ii++]=s+1; idx[ii++]=f+1;
}
}
Mesh* m = create_mesh(dev, v, vc, idx, ic, sizeof(V));
free(v); free(idx);
return m;
}
// OBJ 加载器
Mesh* mesh_load_obj(ID3D11Device* dev, const char* path) {
FILE* f = fopen(path, "r");
if (!f) return NULL;
// 先统计顶点和面数
int max_v = 4096, max_f = 8192;
float* verts = (float*)malloc(max_v * 3 * sizeof(float));
float* uvs = (float*)malloc(max_v * 2 * sizeof(float));
int vert_count = 0, uv_count = 0;
int* face_idx = (int*)malloc(max_f * 3 * sizeof(int));
int face_count = 0;
char line[256];
while (fgets(line, sizeof(line), f)) {
if (line[0] == 'v' && line[1] == ' ') {
float x, y, z;
if (sscanf(line + 2, "%f %f %f", &x, &y, &z) == 3 && vert_count < max_v) {
verts[vert_count * 3] = x;
verts[vert_count * 3 + 1] = y;
verts[vert_count * 3 + 2] = z;
vert_count++;
}
} else if (line[0] == 'v' && line[1] == 't') {
float u, v;
if (sscanf(line + 3, "%f %f", &u, &v) == 2 && uv_count < max_v) {
uvs[uv_count * 2] = u;
uvs[uv_count * 2 + 1] = v;
uv_count++;
}
} else if (line[0] == 'f' && line[1] == ' ') {
// 解析面: f v/vt v/vt v/vt 或 f v v v
int v0, v1, v2;
char* p = line + 2;
v0 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v1 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
v2 = atoi(p) - 1;
if (v0 >= 0 && v1 >= 0 && v2 >= 0 && face_count < max_f) {
face_idx[face_count * 3] = v0;
face_idx[face_count * 3 + 1] = v1;
face_idx[face_count * 3 + 2] = v2;
face_count++;
}
}
}
fclose(f);
if (vert_count == 0 || face_count == 0) {
free(verts); free(uvs); free(face_idx);
return NULL;
}
// 构建去重顶点 (简单方案: 每个面一个顶点)
int out_vcount = face_count * 3;
Vertex* out_verts = (Vertex*)calloc(out_vcount, sizeof(Vertex));
uint32_t* out_idx = (uint32_t*)malloc(face_count * 3 * sizeof(uint32_t));
for (int i = 0; i < face_count; i++) {
for (int j = 0; j < 3; j++) {
int vi = face_idx[i * 3 + j];
int oi = i * 3 + j;
if (vi < vert_count) {
out_verts[oi].pos[0] = verts[vi * 3];
out_verts[oi].pos[1] = verts[vi * 3 + 1];
out_verts[oi].pos[2] = verts[vi * 3 + 2];
}
out_verts[oi].color[0] = out_verts[oi].color[1] = out_verts[oi].color[2] = 1.0f;
out_verts[oi].color[3] = 1.0f;
out_idx[oi] = oi;
}
}
// 简单法线计算 (面法线)
for (int i = 0; i < face_count; i++) {
float* p0 = out_verts[i * 3].pos;
float* p1 = out_verts[i * 3 + 1].pos;
float* p2 = out_verts[i * 3 + 2].pos;
float e1[3] = {p1[0] - p0[0], p1[1] - p0[1], p1[2] - p0[2]};
float e2[3] = {p2[0] - p0[0], p2[1] - p0[1], p2[2] - p0[2]};
float nx = e1[1]*e2[2] - e1[2]*e2[1];
float ny = e1[2]*e2[0] - e1[0]*e2[2];
float nz = e1[0]*e2[1] - e1[1]*e2[0];
float nl = sqrtf(nx*nx + ny*ny + nz*nz);
if (nl > 0.0001f) { nx /= nl; ny /= nl; nz /= nl; }
for (int j = 0; j < 3; j++) {
out_verts[i * 3 + j].norm[0] = nx;
out_verts[i * 3 + j].norm[1] = ny;
out_verts[i * 3 + j].norm[2] = nz;
}
}
Mesh* m = create_mesh(dev, out_verts, out_vcount, out_idx, face_count * 3, sizeof(Vertex));
free(out_verts); free(out_idx); free(verts); free(uvs); free(face_idx);
return m;
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
typedef struct {
float pos[3];
float norm[3];
float uv[2];
float color[4];
} Vertex;
typedef struct {
ID3D11Buffer* vb;
ID3D11Buffer* ib;
uint32_t vcount;
uint32_t icount;
uint32_t stride;
} Mesh;
Mesh* mesh_create_cube(ID3D11Device* dev);
Mesh* mesh_create_plane(ID3D11Device* dev, float size);
Mesh* mesh_create_sphere(ID3D11Device* dev, float r, int seg);
Mesh* mesh_load_obj(ID3D11Device* dev, const char* path);
void mesh_destroy(Mesh* m);
void mesh_draw(Mesh* m, ID3D11DeviceContext* ctx);
+118
View File
@@ -0,0 +1,118 @@
#include "renderer.h"
#include "../Core/logger.h"
#include <stdlib.h>
#include <string.h>
static void fill_constant_buffer(float* cb, const Mat4* world, const Mat4* wvp,
Vec3 light_dir, Vec4 light_color, Vec4 ambient_color,
int use_texture, Vec4 diffuse_color) {
memcpy(cb + 0, world->m, sizeof(float) * 16);
memcpy(cb + 16, wvp->m, sizeof(float) * 16);
cb[32] = light_dir.x; cb[33] = light_dir.y; cb[34] = light_dir.z; cb[35] = 0.0f;
cb[36] = light_color.x; cb[37] = light_color.y; cb[38] = light_color.z; cb[39] = light_color.w;
cb[40] = ambient_color.x; cb[41] = ambient_color.y; cb[42] = ambient_color.z; cb[43] = ambient_color.w;
cb[44] = use_texture ? 1.0f : 0.0f; cb[45] = 0.0f; cb[46] = 0.0f; cb[47] = 0.0f;
cb[48] = diffuse_color.x; cb[49] = diffuse_color.y; cb[50] = diffuse_color.z; cb[51] = diffuse_color.w;
}
Renderer* renderer_create(GraphicsDevice* device) {
Renderer* r = (Renderer*)calloc(1, sizeof(Renderer));
r->device = device;
mat4_identity(&r->view);
mat4_identity(&r->projection);
r->light_dir = (Vec3){0.5f, -1.0f, 0.3f};
r->light_color = (Vec4){1,1,1,1};
r->ambient_color = (Vec4){0.2f,0.2f,0.3f,1};
r->clear_color[0] = 0.1f; r->clear_color[1] = 0.1f;
r->clear_color[2] = 0.15f; r->clear_color[3] = 1.0f;
return r;
}
void renderer_destroy(Renderer* r) { free(r); }
void renderer_begin_frame(Renderer* r) {
ID3D11DeviceContext* ctx = r->device->context;
graphics_device_clear(r->device, r->clear_color[0], r->clear_color[1], r->clear_color[2], r->clear_color[3]);
D3D11_VIEWPORT vp = {0};
vp.Width = (float)r->device->width;
vp.Height = (float)r->device->height;
vp.MaxDepth = 1.0f;
ctx->lpVtbl->RSSetViewports(ctx, 1, &vp);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}
void renderer_end_frame(Renderer* r) {
graphics_device_present(r->device);
}
void renderer_draw_mesh(Renderer* r, Mesh* m) {
if (!r || !m || m->icount == 0) return;
if (!m->vb) { LOG_ERROR("renderer_draw_mesh: null vertex buffer"); return; }
if (!m->ib) { LOG_ERROR("renderer_draw_mesh: null index buffer"); return; }
ID3D11DeviceContext* ctx = r->device->context;
UINT stride = sizeof(Vertex);
UINT offset = 0;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &m->vb, &stride, &offset);
ctx->lpVtbl->IASetIndexBuffer(ctx, m->ib, DXGI_FORMAT_R32_UINT, 0);
ctx->lpVtbl->DrawIndexed(ctx, m->icount, 0, 0);
}
void renderer_draw_mesh_with_shader(Renderer* r, Mesh* m, Shader* s, const Mat4* world, Texture* t) {
if (!m || !s || !r) return;
ID3D11DeviceContext* ctx = r->device->context;
shader_bind(s, ctx);
Mat4 wvp;
mat4_multiply(world, &r->view, &wvp);
mat4_multiply(&wvp, &r->projection, &wvp);
float cb[64];
memset(cb, 0, sizeof(cb));
fill_constant_buffer(cb, world, &wvp, r->light_dir, r->light_color, r->ambient_color, t ? 1 : 0, (Vec4){1,1,1,1});
shader_set_matrix(s, ctx, cb);
if (t) texture_bind(t, ctx, 0);
renderer_draw_mesh(r, m);
}
void renderer_draw_mesh_with_material(Renderer* r, Mesh* m, Material* mat, const Mat4* world) {
if (!m || !mat || !mat->shader || !r) return;
ID3D11DeviceContext* ctx = r->device->context;
shader_bind(mat->shader, ctx);
Mat4 wvp;
mat4_multiply(world, &r->view, &wvp);
mat4_multiply(&wvp, &r->projection, &wvp);
float cb[64];
memset(cb, 0, sizeof(cb));
fill_constant_buffer(cb, world, &wvp, r->light_dir, r->light_color, r->ambient_color,
mat->use_texture, mat->color);
shader_set_matrix(mat->shader, ctx, cb);
if (mat->use_texture && mat->texture) texture_bind(mat->texture, ctx, 0);
renderer_draw_mesh(r, m);
}
void renderer_set_view(Renderer* r, const Mat4* v) { r->view = *v; }
void renderer_set_projection(Renderer* r, const Mat4* p) { r->projection = *p; }
void renderer_set_light(Renderer* r, Vec3 d, Vec4 c, Vec4 a) { r->light_dir=d; r->light_color=c; r->ambient_color=a; }
void renderer_set_clear_color(Renderer* r, float rc, float g, float b, float a) {
if (!r) return;
r->clear_color[0] = rc; r->clear_color[1] = g;
r->clear_color[2] = b; r->clear_color[3] = a;
}
GraphicsDevice* renderer_get_device(Renderer* r) { return r ? r->device : NULL; }
void renderer_get_view_projection(const Renderer* r, Mat4* out) {
if (!r || !out) return;
mat4_multiply(&r->view, &r->projection, out);
}
void renderer_resize(Renderer* r, int width, int height) {
if (r && r->device && width > 0 && height > 0) {
graphics_device_resize(r->device, width, height);
}
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "graphics_device.h"
#include "shader.h"
#include "mesh.h"
#include "texture.h"
#include "camera.h"
#include "material.h"
#include "../include/engine_api.h"
#include "../ASM/math_c_fallback.h"
typedef struct Renderer Renderer;
struct Renderer {
GraphicsDevice* device;
Shader* current_shader;
Mat4 view;
Mat4 projection;
Vec3 light_dir;
Vec4 light_color;
Vec4 ambient_color;
float clear_color[4];
};
Renderer* renderer_create(GraphicsDevice* device);
void renderer_destroy(Renderer* renderer);
void renderer_begin_frame(Renderer* renderer);
void renderer_end_frame(Renderer* renderer);
void renderer_draw_mesh(Renderer* renderer, Mesh* mesh);
void renderer_draw_mesh_with_shader(Renderer* renderer, Mesh* mesh, Shader* shader, const Mat4* world, Texture* tex);
void renderer_draw_mesh_with_material(Renderer* renderer, Mesh* mesh, Material* material, const Mat4* world);
void renderer_set_view(Renderer* renderer, const Mat4* view);
void renderer_set_projection(Renderer* renderer, const Mat4* projection);
void renderer_set_light(Renderer* renderer, Vec3 dir, Vec4 color, Vec4 ambient);
void renderer_set_clear_color(Renderer* renderer, float r, float g, float b, float a);
GraphicsDevice* renderer_get_device(Renderer* renderer);
void renderer_resize(Renderer* renderer, int width, int height);
void renderer_get_view_projection(const Renderer* renderer, Mat4* out);
+84
View File
@@ -0,0 +1,84 @@
#include "shader.h"
#include "../Core/logger.h"
#include <d3dcompiler.h>
#include <stdlib.h>
#include <string.h>
static const char* default_vs =
"struct VS_IN{float3 pos:POSITION;float3 n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
"struct PS_IN{float4 pos:SV_POSITION;float3 world_n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
"cbuffer CB:register(b0){float4x4 World;float4x4 WVP;float4 LightDir;float4 LightColor;float4 AmbientColor;float4 UseTexture;float4 DiffuseColor;};"
"PS_IN main(VS_IN i){PS_IN o;o.pos=mul(float4(i.pos,1),WVP);o.world_n=normalize(mul(float4(i.n,0),World).xyz);o.uv=i.uv;o.col=i.col*DiffuseColor;return o;}";
static const char* default_ps =
"struct PS_IN{float4 pos:SV_POSITION;float3 world_n:NORMAL;float2 uv:TEXCOORD;float4 col:COLOR;};"
"cbuffer CB:register(b0){float4x4 World;float4x4 WVP;float4 LightDir;float4 LightColor;float4 AmbientColor;float4 UseTexture;float4 DiffuseColor;};"
"Texture2D tex:register(t0);SamplerState samp:register(s0);"
"float4 main(PS_IN i):SV_TARGET{float3 ld=normalize(LightDir.xyz);float diff=saturate(dot(i.world_n,ld));float4 light=AmbientColor+LightColor*diff;float4 albedo=i.col;if(UseTexture.x>0.5)albedo=albedo*tex.Sample(samp,i.uv);return albedo*light;}";
static ID3DBlob* compile_shader(const char* code, uint32_t len, const char* target) {
ID3DBlob *blob = NULL, *err = NULL;
UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_PACK_MATRIX_ROW_MAJOR;
D3DCompile(code, len, NULL, NULL, NULL, "main", target, flags, 0, &blob, &err);
if (err) {
const char* msg = (const char*)err->lpVtbl->GetBufferPointer(err);
LOG_ERROR("Shader compile error (%s): %s", target, msg);
err->lpVtbl->Release(err);
}
return blob;
}
Shader* shader_create(void) {
return (Shader*)calloc(1, sizeof(Shader));
}
void shader_destroy(Shader* s) {
if (!s) return;
if (s->cb) s->cb->lpVtbl->Release(s->cb);
if (s->layout) s->layout->lpVtbl->Release(s->layout);
if (s->ps) s->ps->lpVtbl->Release(s->ps);
if (s->vs) s->vs->lpVtbl->Release(s->vs);
free(s);
}
int shader_compile(Shader* s, ID3D11Device* dev, const char* vs_code, const char* ps_code, uint32_t vs_len, uint32_t ps_len) {
if (!vs_code || !ps_code) {
vs_code = default_vs; ps_code = default_ps;
vs_len = (uint32_t)strlen(default_vs); ps_len = (uint32_t)strlen(default_ps);
}
ID3DBlob* vs_blob = compile_shader(vs_code, vs_len, "vs_5_0");
ID3DBlob* ps_blob = compile_shader(ps_code, ps_len, "ps_5_0");
if (!vs_blob || !ps_blob) {
LOG_ERROR("Failed to compile default shader (vs=%p ps=%p)", (void*)vs_blob, (void*)ps_blob);
if (vs_blob) vs_blob->lpVtbl->Release(vs_blob);
if (ps_blob) ps_blob->lpVtbl->Release(ps_blob);
return 0;
}
dev->lpVtbl->CreateVertexShader(dev, vs_blob->lpVtbl->GetBufferPointer(vs_blob), vs_blob->lpVtbl->GetBufferSize(vs_blob), NULL, &s->vs);
dev->lpVtbl->CreatePixelShader(dev, ps_blob->lpVtbl->GetBufferPointer(ps_blob), ps_blob->lpVtbl->GetBufferSize(ps_blob), NULL, &s->ps);
D3D11_INPUT_ELEMENT_DESC layout[] = {
{"POSITION",0,DXGI_FORMAT_R32G32B32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0},
{"NORMAL",0,DXGI_FORMAT_R32G32B32_FLOAT,0,12,D3D11_INPUT_PER_VERTEX_DATA,0},
{"TEXCOORD",0,DXGI_FORMAT_R32G32_FLOAT,0,24,D3D11_INPUT_PER_VERTEX_DATA,0},
{"COLOR",0,DXGI_FORMAT_R32G32B32A32_FLOAT,0,32,D3D11_INPUT_PER_VERTEX_DATA,0},
};
dev->lpVtbl->CreateInputLayout(dev, layout, 4, vs_blob->lpVtbl->GetBufferPointer(vs_blob), vs_blob->lpVtbl->GetBufferSize(vs_blob), &s->layout);
D3D11_BUFFER_DESC bd = {0};
bd.ByteWidth = 256; bd.Usage = D3D11_USAGE_DEFAULT; bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
dev->lpVtbl->CreateBuffer(dev, &bd, NULL, &s->cb);
vs_blob->lpVtbl->Release(vs_blob);
ps_blob->lpVtbl->Release(ps_blob);
return 1;
}
void shader_bind(Shader* s, ID3D11DeviceContext* ctx) {
ctx->lpVtbl->VSSetShader(ctx, s->vs, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, s->ps, NULL, 0);
ctx->lpVtbl->IASetInputLayout(ctx, s->layout);
ctx->lpVtbl->VSSetConstantBuffers(ctx, 0, 1, &s->cb);
ctx->lpVtbl->PSSetConstantBuffers(ctx, 0, 1, &s->cb);
}
void shader_set_matrix(Shader* s, ID3D11DeviceContext* ctx, const float* data) {
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)s->cb, 0, NULL, data, 0, 0);
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <d3d11.h>
#include <stdint.h>
typedef struct {
ID3D11VertexShader* vs;
ID3D11PixelShader* ps;
ID3D11InputLayout* layout;
ID3D11Buffer* cb;
} Shader;
Shader* shader_create(void);
void shader_destroy(Shader* s);
int shader_compile(Shader* s, ID3D11Device* dev, const char* vs_code, const char* ps_code, uint32_t vs_len, uint32_t ps_len);
void shader_bind(Shader* s, ID3D11DeviceContext* ctx);
void shader_set_matrix(Shader* s, ID3D11DeviceContext* ctx, const float* data);
+231
View File
@@ -0,0 +1,231 @@
#include "texture.h"
#include <wincodec.h>
#include <stdlib.h>
#include <stdio.h>
#pragma comment(lib, "windowscodecs.lib")
// 为 MinGW 提供 COM 助手宏(MSVC 已内联提供)
#ifndef _MSC_VER
#define ID3D11ShaderResourceView_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11Texture2D_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11Device_CreateTexture2D(p,a,b,c) (p)->lpVtbl->CreateTexture2D(p,a,b,c)
#define ID3D11Device_CreateShaderResourceView(p,a,b,c) (p)->lpVtbl->CreateShaderResourceView(p,a,b,c)
#define ID3D11DeviceContext_GetDevice(p,a) (p)->lpVtbl->GetDevice(p,a)
#define ID3D11DeviceContext_PSSetShaderResources(p,a,b,c) (p)->lpVtbl->PSSetShaderResources(p,a,b,c)
#define ID3D11DeviceContext_PSSetSamplers(p,a,b,c) (p)->lpVtbl->PSSetSamplers(p,a,b,c)
#define ID3D11Device_CreateSamplerState(p,a,b) (p)->lpVtbl->CreateSamplerState(p,a,b)
#define IWICBitmapDecoder_GetFrame(p,a,b) (p)->lpVtbl->GetFrame(p,a,b)
#define IWICBitmapFrameDecode_GetSize(p,a,b) (p)->lpVtbl->GetSize(p,a,b)
#define IWICImagingFactory_CreateFormatConverter(p,a) (p)->lpVtbl->CreateFormatConverter(p,a)
#define IWICFormatConverter_Initialize(p,a,b,c,d,e,f) (p)->lpVtbl->Initialize(p,a,b,c,d,e,f)
#define IWICFormatConverter_CopyPixels(p,a,b,c,d) (p)->lpVtbl->CopyPixels(p,a,b,c,d)
#define IWICFormatConverter_Release(p) (p)->lpVtbl->Release(p)
#define IWICBitmapFrameDecode_Release(p) (p)->lpVtbl->Release(p)
#define IWICImagingFactory_CreateDecoderFromFilename(p,a,b,c,d,e) (p)->lpVtbl->CreateDecoderFromFilename(p,a,b,c,d,e)
#define IWICBitmapDecoder_Release(p) (p)->lpVtbl->Release(p)
#define IWICImagingFactory_CreateStream(p,a) (p)->lpVtbl->CreateStream(p,a)
#define IWICStream_InitializeFromMemory(p,a,b) (p)->lpVtbl->InitializeFromMemory(p,a,b)
#define IWICImagingFactory_CreateDecoderFromStream(p,a,b,c,d) (p)->lpVtbl->CreateDecoderFromStream(p,a,b,c,d)
#define IWICStream_Release(p) (p)->lpVtbl->Release(p)
#endif
static IWICImagingFactory* wic_factory = NULL;
static HRESULT ensure_wic(void) {
if (wic_factory) return S_OK;
return CoCreateInstance(&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER,
&IID_IWICImagingFactory, (void**)&wic_factory);
}
Texture* texture_create(void) {
return (Texture*)calloc(1, sizeof(Texture));
}
void texture_destroy(Texture* texture) {
if (!texture) return;
if (texture->srv) ID3D11ShaderResourceView_Release(texture->srv);
if (texture->texture) ID3D11Texture2D_Release(texture->texture);
free(texture);
}
static Texture* create_from_wic(ID3D11Device* device, IWICBitmapDecoder* decoder) {
IWICBitmapFrameDecode* frame = NULL;
IWICBitmapDecoder_GetFrame(decoder, 0, &frame);
uint32_t w, h;
IWICBitmapFrameDecode_GetSize(frame, &w, &h);
IWICFormatConverter* converter = NULL;
IWICImagingFactory_CreateFormatConverter(wic_factory, &converter);
IWICFormatConverter_Initialize(converter, (IWICBitmapSource*)frame,
&GUID_WICPixelFormat32bppRGBA, WICBitmapDitherTypeNone, NULL, 0.0, WICBitmapPaletteTypeCustom);
uint8_t* pixels = (uint8_t*)malloc(w * h * 4);
IWICFormatConverter_CopyPixels(converter, NULL, w * 4, w * h * 4, pixels);
IWICFormatConverter_Release(converter);
IWICBitmapFrameDecode_Release(frame);
D3D11_TEXTURE2D_DESC desc = {0};
desc.Width = w;
desc.Height = h;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA init = {pixels, w * 4, 0};
Texture* tex = texture_create();
ID3D11Device_CreateTexture2D(device, &desc, &init, &tex->texture);
D3D11_SHADER_RESOURCE_VIEW_DESC srvd = {0};
srvd.Format = desc.Format;
srvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvd.Texture2D.MipLevels = 1;
ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource*)tex->texture, &srvd, &tex->srv);
tex->width = w;
tex->height = h;
tex->mip_levels = 1;
free(pixels);
return tex;
}
Texture* texture_load_from_file(ID3D11Device* device, const char* path) {
if (FAILED(ensure_wic())) return NULL;
IWICBitmapDecoder* decoder = NULL;
wchar_t wpath[512];
MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, 512);
HRESULT hr = IWICImagingFactory_CreateDecoderFromFilename(wic_factory, wpath, NULL,
GENERIC_READ, WICDecodeMetadataCacheOnLoad, &decoder);
if (FAILED(hr)) return NULL;
Texture* tex = create_from_wic(device, decoder);
IWICBitmapDecoder_Release(decoder);
return tex;
}
Texture* texture_load_from_memory(ID3D11Device* device, const uint8_t* data, uint32_t size) {
if (FAILED(ensure_wic())) return NULL;
IWICStream* stream = NULL;
IWICImagingFactory_CreateStream(wic_factory, &stream);
IWICStream_InitializeFromMemory(stream, (BYTE*)data, size);
IWICBitmapDecoder* decoder = NULL;
IWICImagingFactory_CreateDecoderFromStream(wic_factory, (IStream*)stream, NULL,
WICDecodeMetadataCacheOnLoad, &decoder);
IWICStream_Release(stream);
if (!decoder) return NULL;
Texture* tex = create_from_wic(device, decoder);
IWICBitmapDecoder_Release(decoder);
return tex;
}
Texture* texture_create_solid(ID3D11Device* device, uint32_t w, uint32_t h,
uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
uint8_t* pixels = (uint8_t*)malloc(w * h * 4);
for (uint32_t i = 0; i < w * h; i++) {
pixels[i * 4 + 0] = r;
pixels[i * 4 + 1] = g;
pixels[i * 4 + 2] = b;
pixels[i * 4 + 3] = a;
}
D3D11_TEXTURE2D_DESC desc = {0};
desc.Width = w;
desc.Height = h;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA init = {pixels, w * 4, 0};
Texture* tex = texture_create();
ID3D11Device_CreateTexture2D(device, &desc, &init, &tex->texture);
D3D11_SHADER_RESOURCE_VIEW_DESC srvd = {0};
srvd.Format = desc.Format;
srvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvd.Texture2D.MipLevels = 1;
ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource*)tex->texture, &srvd, &tex->srv);
tex->width = w;
tex->height = h;
free(pixels);
return tex;
}
Texture* texture_create_checkerboard(ID3D11Device* device, uint32_t size, uint32_t checks) {
uint8_t* pixels = (uint8_t*)malloc(size * size * 4);
uint32_t cell = size / checks;
for (uint32_t y = 0; y < size; y++) {
for (uint32_t x = 0; x < size; x++) {
uint8_t c = ((x / cell) + (y / cell)) % 2 ? 200 : 55;
uint32_t i = (y * size + x) * 4;
pixels[i] = pixels[i + 1] = pixels[i + 2] = c;
pixels[i + 3] = 255;
}
}
D3D11_TEXTURE2D_DESC desc = {0};
desc.Width = size;
desc.Height = size;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA init = {pixels, size * 4, 0};
Texture* tex = texture_create();
ID3D11Device_CreateTexture2D(device, &desc, &init, &tex->texture);
D3D11_SHADER_RESOURCE_VIEW_DESC srvd = {0};
srvd.Format = desc.Format;
srvd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvd.Texture2D.MipLevels = 1;
ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource*)tex->texture, &srvd, &tex->srv);
tex->width = size;
tex->height = size;
free(pixels);
return tex;
}
void texture_bind(Texture* texture, ID3D11DeviceContext* ctx, uint32_t slot) {
if (!texture || !texture->srv) return;
static ID3D11SamplerState* default_sampler = NULL;
if (!default_sampler) {
ID3D11Device* device = NULL;
ctx->lpVtbl->GetDevice(ctx, &device);
if (device) {
D3D11_SAMPLER_DESC sd = {0};
sd.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sd.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sd.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sd.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
sd.ComparisonFunc = D3D11_COMPARISON_NEVER;
sd.MinLOD = 0;
sd.MaxLOD = D3D11_FLOAT32_MAX;
device->lpVtbl->CreateSamplerState(device, &sd, &default_sampler);
device->lpVtbl->Release(device);
}
}
ctx->lpVtbl->PSSetShaderResources(ctx, slot, 1, &texture->srv);
ctx->lpVtbl->PSSetSamplers(ctx, slot, 1, &default_sampler);
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
// ============================================================================
// texture.h - 纹理加载 (WIC)
// ============================================================================
#include <d3d11.h>
#include <stdint.h>
typedef struct {
ID3D11Texture2D* texture;
ID3D11ShaderResourceView* srv;
uint32_t width;
uint32_t height;
uint32_t mip_levels;
} Texture;
// 创建/销毁
Texture* texture_create(void);
void texture_destroy(Texture* texture);
// 从文件加载
Texture* texture_load_from_file(ID3D11Device* device, const char* path);
// 从内存加载
Texture* texture_load_from_memory(ID3D11Device* device,
const uint8_t* data, uint32_t size);
// 创建纯色纹理
Texture* texture_create_solid(ID3D11Device* device, uint32_t w, uint32_t h,
uint8_t r, uint8_t g, uint8_t b, uint8_t a);
// 创建棋盘格
Texture* texture_create_checkerboard(ID3D11Device* device, uint32_t size, uint32_t checks);
// 绑定
void texture_bind(Texture* texture, ID3D11DeviceContext* ctx, uint32_t slot);
+226
View File
@@ -0,0 +1,226 @@
// ============================================================================
// scene_graph.c - 场景图实现
// ============================================================================
#include "scene_graph.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
void scene_init(Scene* scene) {
memset(scene, 0, sizeof(Scene));
scene->next_id = 1;
// 创建根节点
scene->root = &scene->nodes[0];
memset(scene->root, 0, sizeof(SceneNode));
scene->root->id = scene->next_id++;
strcpy(scene->root->name, "Root");
scene->root->scale = (Vec3){1, 1, 1};
scene->root->visible = 1;
scene->root->dirty = 1;
scene->node_count = 1;
}
void scene_destroy(Scene* scene) {
// 清空子节点引用 (不释放静态数组)
for (int i = 0; i < scene->node_count; i++) {
scene->nodes[i].parent = NULL;
scene->nodes[i].child_count = 0;
}
scene->node_count = 0;
}
SceneNode* scene_create_node(Scene* scene, const char* name) {
if (scene->node_count >= MAX_NODES) return NULL;
SceneNode* node = &scene->nodes[scene->node_count++];
memset(node, 0, sizeof(SceneNode));
node->id = scene->next_id++;
strncpy(node->name, name, 63);
node->scale = (Vec3){1, 1, 1};
node->visible = 1;
node->dirty = 1;
return node;
}
void scene_destroy_node(Scene* scene, SceneNode* node) {
if (!node || node == scene->root) return;
// 移除子引用
if (node->parent) {
scene_remove_child(node->parent, node);
}
// 递归销毁子节点
while (node->child_count > 0) {
scene_destroy_node(scene, node->children[0]);
}
node->id = 0; // 标记为空
}
SceneNode* scene_find_node(Scene* scene, int id) {
for (int i = 0; i < scene->node_count; i++) {
if (scene->nodes[i].id == id) return &scene->nodes[i];
}
return NULL;
}
SceneNode* scene_find_by_name(Scene* scene, const char* name) {
for (int i = 0; i < scene->node_count; i++) {
if (strcmp(scene->nodes[i].name, name) == 0) return &scene->nodes[i];
}
return NULL;
}
void scene_set_parent(SceneNode* child, SceneNode* parent) {
if (!child || !parent || child == parent) return;
if (child->parent) scene_remove_child(child->parent, child);
child->parent = parent;
if (parent->child_count < MAX_CHILDREN) {
parent->children[parent->child_count++] = child;
}
child->dirty = 1;
}
void scene_add_child(SceneNode* parent, SceneNode* child) {
scene_set_parent(child, parent);
}
void scene_remove_child(SceneNode* parent, SceneNode* child) {
if (!parent || !child) return;
for (int i = 0; i < parent->child_count; i++) {
if (parent->children[i] == child) {
parent->children[i] = parent->children[parent->child_count - 1];
parent->child_count--;
child->parent = NULL;
child->dirty = 1;
return;
}
}
}
void scene_set_position(SceneNode* node, float x, float y, float z) {
node->position = (Vec3){x, y, z};
node->dirty = 1;
}
void scene_set_rotation_euler(SceneNode* node, float rx, float ry, float rz) {
// 欧拉角转四元数 (YXZ 顺序)
float cr = cosf(rx * 0.5f), sr = sinf(rx * 0.5f);
float cp = cosf(ry * 0.5f), sp = sinf(ry * 0.5f);
float cy = cosf(rz * 0.5f), sy = sinf(rz * 0.5f);
node->rotation = (Quat){
sr * cp * cy - cr * sp * sy,
cr * sp * cy + sr * cp * sy,
cr * cp * sy - sr * sp * cy,
cr * cp * cy + sr * sp * sy
};
node->dirty = 1;
}
void scene_set_scale(SceneNode* node, float sx, float sy, float sz) {
node->scale = (Vec3){sx, sy, sz};
node->dirty = 1;
}
void scene_set_rotation_quat(SceneNode* node, const Quat* q) {
node->rotation = *q;
node->dirty = 1;
}
void scene_mark_dirty(SceneNode* node) {
node->dirty = 1;
for (int i = 0; i < node->child_count; i++) {
scene_mark_dirty(node->children[i]);
}
}
static void update_local_matrix(SceneNode* node) {
// 旋转矩阵
Mat4 rot;
quat_to_matrix(&node->rotation, &rot);
// 缩放矩阵
Mat4 scl;
mat4_identity(&scl);
scl.m[0][0] = node->scale.x;
scl.m[1][1] = node->scale.y;
scl.m[2][2] = node->scale.z;
// 平移矩阵
Mat4 trl;
mat4_translate(node->position.x, node->position.y, node->position.z, &trl);
// local = trl * rot * scl
Mat4 tmp;
mat4_multiply(&rot, &scl, &tmp);
mat4_multiply(&trl, &tmp, &node->local_matrix);
}
static void update_world_matrices(SceneNode* node) {
if (node->dirty) {
update_local_matrix(node);
if (node->parent) {
mat4_multiply(&node->parent->world_matrix, &node->local_matrix, &node->world_matrix);
} else {
node->world_matrix = node->local_matrix;
}
node->dirty = 0;
}
for (int i = 0; i < node->child_count; i++) {
update_world_matrices(node->children[i]);
}
}
void scene_update(Scene* scene) {
if (scene->root) {
update_world_matrices(scene->root);
}
}
void scene_get_world_position(SceneNode* node, Vec3* out) {
out->x = node->world_matrix.m[3][0];
out->y = node->world_matrix.m[3][1];
out->z = node->world_matrix.m[3][2];
}
void scene_get_world_rotation(SceneNode* node, Quat* out) {
// 从世界矩阵提取四元数
Mat4 m = node->world_matrix;
float tr = m.m[0][0] + m.m[1][1] + m.m[2][2];
if (tr > 0) {
float s = 0.5f / sqrtf(tr + 1.0f);
out->w = 0.25f / s;
out->x = (m.m[2][1] - m.m[1][2]) * s;
out->y = (m.m[0][2] - m.m[2][0]) * s;
out->z = (m.m[1][0] - m.m[0][1]) * s;
} else if (m.m[0][0] > m.m[1][1] && m.m[0][0] > m.m[2][2]) {
float s = 2.0f * sqrtf(1.0f + m.m[0][0] - m.m[1][1] - m.m[2][2]);
out->w = (m.m[2][1] - m.m[1][2]) / s;
out->x = 0.25f * s;
out->y = (m.m[0][1] + m.m[1][0]) / s;
out->z = (m.m[0][2] + m.m[2][0]) / s;
} else if (m.m[1][1] > m.m[2][2]) {
float s = 2.0f * sqrtf(1.0f + m.m[1][1] - m.m[0][0] - m.m[2][2]);
out->w = (m.m[0][2] - m.m[2][0]) / s;
out->x = (m.m[0][1] + m.m[1][0]) / s;
out->y = 0.25f * s;
out->z = (m.m[1][2] + m.m[2][1]) / s;
} else {
float s = 2.0f * sqrtf(1.0f + m.m[2][2] - m.m[0][0] - m.m[1][1]);
out->w = (m.m[1][0] - m.m[0][1]) / s;
out->x = (m.m[0][2] + m.m[2][0]) / s;
out->y = (m.m[1][2] + m.m[2][1]) / s;
out->z = 0.25f * s;
}
}
void scene_get_world_scale(SceneNode* node, Vec3* out) {
// 从世界矩阵提取缩放 (各列向量的长度)
out->x = sqrtf(node->world_matrix.m[0][0] * node->world_matrix.m[0][0] +
node->world_matrix.m[0][1] * node->world_matrix.m[0][1] +
node->world_matrix.m[0][2] * node->world_matrix.m[0][2]);
out->y = sqrtf(node->world_matrix.m[1][0] * node->world_matrix.m[1][0] +
node->world_matrix.m[1][1] * node->world_matrix.m[1][1] +
node->world_matrix.m[1][2] * node->world_matrix.m[1][2]);
out->z = sqrtf(node->world_matrix.m[2][0] * node->world_matrix.m[2][0] +
node->world_matrix.m[2][1] * node->world_matrix.m[2][1] +
node->world_matrix.m[2][2] * node->world_matrix.m[2][2]);
}
+77
View File
@@ -0,0 +1,77 @@
#pragma once
// ============================================================================
// scene_graph.h - 场景图: 节点树 + 变换继承
// ============================================================================
#include "../ASM/math_c_fallback.h"
#include <stdint.h>
#define MAX_CHILDREN 64
#define MAX_NODES 1024
// 场景节点
typedef struct SceneNode {
int id;
char name[64];
// 本地变换
Vec3 position;
Quat rotation;
Vec3 scale;
// 世界矩阵 (每帧由 scene_update 计算)
Mat4 world_matrix;
Mat4 local_matrix;
// 层级
struct SceneNode* parent;
struct SceneNode* children[MAX_CHILDREN];
int child_count;
// 标志
int visible;
int dirty; // 需要重新计算矩阵
// 用户数据
void* user_data;
int user_type;
} SceneNode;
// 场景
typedef struct Scene {
SceneNode nodes[MAX_NODES];
int node_count;
SceneNode* root;
int next_id;
} Scene;
// 初始化/销毁
void scene_init(Scene* scene);
void scene_destroy(Scene* scene);
// 节点管理
SceneNode* scene_create_node(Scene* scene, const char* name);
void scene_destroy_node(Scene* scene, SceneNode* node);
SceneNode* scene_find_node(Scene* scene, int id);
SceneNode* scene_find_by_name(Scene* scene, const char* name);
// 层级
void scene_set_parent(SceneNode* child, SceneNode* parent);
void scene_add_child(SceneNode* parent, SceneNode* child);
void scene_remove_child(SceneNode* parent, SceneNode* child);
// 变换
void scene_set_position(SceneNode* node, float x, float y, float z);
void scene_set_rotation_euler(SceneNode* node, float rx, float ry, float rz);
void scene_set_scale(SceneNode* node, float sx, float sy, float sz);
void scene_set_rotation_quat(SceneNode* node, const Quat* q);
void scene_mark_dirty(SceneNode* node);
// 每帧更新
void scene_update(Scene* scene);
// 获取世界变换
void scene_get_world_position(SceneNode* node, Vec3* out);
void scene_get_world_rotation(SceneNode* node, Quat* out);
void scene_get_world_scale(SceneNode* node, Vec3* out);
+65
View File
@@ -0,0 +1,65 @@
#include "scene_render.h"
#include <stdlib.h>
#include <string.h>
void scene_node_set_mesh_renderer(SceneNode* node, MeshRenderer* renderer) {
if (!node) return;
node->user_type = 1;
node->user_data = renderer;
}
MeshRenderer* scene_node_get_mesh_renderer(SceneNode* node) {
if (!node || node->user_type != 1) return NULL;
return (MeshRenderer*)node->user_data;
}
static void scene_render_node_recursive(SceneNode* node, Renderer* r) {
if (!node || !node->visible) return;
MeshRenderer* mr = scene_node_get_mesh_renderer(node);
if (mr && mr->mesh && mr->material) {
renderer_draw_mesh_with_material(r, mr->mesh, mr->material, &node->world_matrix);
}
for (int i = 0; i < node->child_count; i++) {
scene_render_node_recursive(node->children[i], r);
}
}
void scene_render(Scene* scene, Renderer* r) {
if (!scene || !r) return;
scene_render_node_recursive(scene->root, r);
}
MeshRenderer* mesh_renderer_create(MeshType type, Mesh* mesh, Material* material) {
MeshRenderer* mr = (MeshRenderer*)calloc(1, sizeof(MeshRenderer));
mr->type = type;
mr->mesh = mesh;
mr->material = material;
mr->path[0] = '\0';
return mr;
}
void mesh_renderer_destroy(MeshRenderer* mr) {
if (!mr) return;
if (mr->material) material_destroy(mr->material);
free(mr);
}
static void destroy_renderers_recursive(SceneNode* node) {
if (!node) return;
MeshRenderer* mr = scene_node_get_mesh_renderer(node);
if (mr) {
mesh_renderer_destroy(mr);
node->user_data = NULL;
node->user_type = 0;
}
for (int i = 0; i < node->child_count; i++) {
destroy_renderers_recursive(node->children[i]);
}
}
void scene_destroy_renderers(Scene* scene) {
if (!scene) return;
destroy_renderers_recursive(scene->root);
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "scene_graph.h"
#include "../Rendering/renderer.h"
#include "../Rendering/mesh.h"
#include "../Rendering/material.h"
#include "../Rendering/shader.h"
#include "../Rendering/texture.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MESH_NONE = 0,
MESH_CUBE = 1,
MESH_SPHERE = 2,
MESH_PLANE = 3,
MESH_OBJ = 4
} MeshType;
typedef struct {
MeshType type;
Mesh* mesh;
Material* material;
char path[256];
} MeshRenderer;
void scene_node_set_mesh_renderer(SceneNode* node, MeshRenderer* renderer);
MeshRenderer* scene_node_get_mesh_renderer(SceneNode* node);
void scene_render_node(SceneNode* node, Renderer* r);
void scene_render(Scene* scene, Renderer* r);
MeshRenderer* mesh_renderer_create(MeshType type, Mesh* mesh, Material* material);
void mesh_renderer_destroy(MeshRenderer* mr);
void scene_destroy_renderers(Scene* scene);
#ifdef __cplusplus
}
#endif
+141
View File
@@ -0,0 +1,141 @@
#include "scene_serialize.h"
#include "scene_render.h"
#include "../ASM/math_c_fallback.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifndef PI
#define PI 3.14159265f
#endif
static int node_index(Scene* scene, SceneNode* node) {
if (!scene || !node) return -1;
return (int)(node - scene->nodes);
}
static const char* mesh_type_name(MeshType t) {
switch (t) {
case MESH_CUBE: return "CUBE";
case MESH_SPHERE: return "SPHERE";
case MESH_PLANE: return "PLANE";
case MESH_OBJ: return "OBJ";
default: return "NONE";
}
}
static MeshType parse_mesh_type(const char* s) {
if (!s) return MESH_NONE;
if (strcmp(s, "CUBE") == 0) return MESH_CUBE;
if (strcmp(s, "SPHERE") == 0) return MESH_SPHERE;
if (strcmp(s, "PLANE") == 0) return MESH_PLANE;
if (strcmp(s, "OBJ") == 0) return MESH_OBJ;
return MESH_NONE;
}
static void write_node(FILE* f, Scene* scene, SceneNode* node) {
if (!node) return;
int parent = (node == scene->root) ? -1 : node_index(scene, node->parent);
MeshRenderer* mr = scene_node_get_mesh_renderer(node);
MeshType type = mr ? mr->type : MESH_NONE;
const char* path = (mr && mr->path[0]) ? mr->path : "-";
float ex = 0.0f, ey = 0.0f, ez = 0.0f;
quat_to_euler(&node->rotation, &ex, &ey, &ez);
fprintf(f, "%s %d %s %s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",
node->name, parent, mesh_type_name(type), path,
node->position.x, node->position.y, node->position.z,
ex * 180.0f / PI, ey * 180.0f / PI, ez * 180.0f / PI,
node->scale.x, node->scale.y, node->scale.z);
for (int i = 0; i < node->child_count; i++) {
write_node(f, scene, node->children[i]);
}
}
int scene_save(Scene* scene, const char* path) {
if (!scene || !path) return 0;
FILE* f = fopen(path, "w");
if (!f) return 0;
fprintf(f, "LITY3D_SCENE 1\n");
write_node(f, scene, scene->root);
fclose(f);
return 1;
}
static Mesh* create_mesh_for_type(MeshType type, ID3D11Device* dev, const char* path) {
switch (type) {
case MESH_CUBE: return mesh_create_cube(dev);
case MESH_SPHERE: return mesh_create_sphere(dev, 1.0f, 16);
case MESH_PLANE: return mesh_create_plane(dev, 10.0f);
case MESH_OBJ: return path ? mesh_load_obj(dev, path) : NULL;
default: return NULL;
}
}
Scene* scene_load(const char* path, ID3D11Device* dev, Shader* default_shader) {
FILE* f = fopen(path, "r");
if (!f) return NULL;
char line[512];
if (!fgets(line, sizeof(line), f)) { fclose(f); return NULL; }
if (strncmp(line, "LITY3D_SCENE", 10) != 0) { fclose(f); return NULL; }
Scene* scene = (Scene*)malloc(sizeof(Scene));
if (!scene) { fclose(f); return NULL; }
scene_init(scene);
int capacity = 64;
SceneNode** nodes = (SceneNode**)malloc(sizeof(SceneNode*) * capacity);
int count = 0;
while (fgets(line, sizeof(line), f)) {
char name[64];
char type_str[16];
char path_str[256];
int parent_id;
float px, py, pz, degx, degy, degz, sx, sy, sz;
if (sscanf(line, "%63s %d %15s %255s %f %f %f %f %f %f %f %f %f",
name, &parent_id, type_str, path_str,
&px, &py, &pz, &degx, &degy, &degz, &sx, &sy, &sz) != 13) {
continue;
}
if (count >= capacity) {
capacity *= 2;
nodes = (SceneNode**)realloc(nodes, sizeof(SceneNode*) * capacity);
}
SceneNode* parent = (parent_id < 0) ? scene->root : nodes[parent_id];
SceneNode* node = scene_create_node(scene, name);
if (!node) continue;
if (parent) scene_set_parent(node, parent);
node->position = (Vec3){px, py, pz};
node->scale = (Vec3){sx, sy, sz};
quat_from_euler(degx * PI / 180.0f, degy * PI / 180.0f, degz * PI / 180.0f, &node->rotation);
MeshType type = parse_mesh_type(type_str);
if (type != MESH_NONE) {
const char* mesh_path = (strcmp(path_str, "-") == 0) ? NULL : path_str;
Mesh* mesh = create_mesh_for_type(type, dev, mesh_path);
if (mesh) {
Material* mat = material_create(default_shader, NULL);
MeshRenderer* mr = mesh_renderer_create(type, mesh, mat);
if (mesh_path) strncpy(mr->path, mesh_path, sizeof(mr->path) - 1);
scene_node_set_mesh_renderer(node, mr);
}
}
nodes[count] = node;
count++;
}
free(nodes);
fclose(f);
scene_update(scene);
return scene;
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "scene_graph.h"
#include "../Rendering/shader.h"
#ifdef __cplusplus
extern "C" {
#endif
int scene_save(Scene* scene, const char* path);
Scene* scene_load(const char* path, ID3D11Device* dev, Shader* default_shader);
#ifdef __cplusplus
}
#endif
+60
View File
@@ -0,0 +1,60 @@
#include "lua_engine.h"
#include <stdlib.h>
#include <stdio.h>
// Lua头文件 (需要单独下载或集成)
// #include <lua.h>
// #include <lualib.h>
// #include <lauxlib.h>
// 由于Lua需要单独集成,这里提供占位实现
// 实际使用时取消注释并链接Lua库
typedef struct lua_State lua_State;
static lua_State* luaL_newstate(void) { return NULL; }
static void luaL_openlibs(lua_State* L) { (void)L; }
static int luaL_dofile(lua_State* L, const char* fn) { (void)L; (void)fn; return -1; }
static int luaL_dostring(lua_State* L, const char* s) { (void)L; (void)s; return -1; }
static void lua_close(lua_State* L) { (void)L; }
LuaEngine* lua_engine_create(void) {
return (LuaEngine*)calloc(1, sizeof(LuaEngine));
}
void lua_engine_destroy(LuaEngine* engine) {
if (!engine) return;
if (engine->L) lua_close(engine->L);
free(engine);
}
int lua_engine_init(LuaEngine* engine) {
engine->L = luaL_newstate();
if (!engine->L) return -1;
luaL_openlibs(engine->L);
engine->initialized = 1;
return 0;
}
int lua_engine_do_file(LuaEngine* engine, const char* path) {
if (!engine->L) return -1;
return luaL_dofile(engine->L, path);
}
int lua_engine_do_string(LuaEngine* engine, const char* code) {
if (!engine->L) return -1;
return luaL_dostring(engine->L, code);
}
void lua_engine_register_function(LuaEngine* engine, const char* name, void* func) {
(void)engine;
(void)name;
(void)func;
// lua_pushcfunction(engine->L, func);
// lua_setglobal(engine->L, name);
}
lua_State* lua_engine_get_state(LuaEngine* engine) {
return engine->L;
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
// ============================================================================
// lua_engine.h - Lua脚本引擎
// ============================================================================
#include <stdint.h>
// 前向声明
struct lua_State;
typedef struct {
struct lua_State* L;
int initialized;
} LuaEngine;
// 创建/销毁
LuaEngine* lua_engine_create(void);
void lua_engine_destroy(LuaEngine* engine);
// 初始化
int lua_engine_init(LuaEngine* engine);
// 执行
int lua_engine_do_file(LuaEngine* engine, const char* path);
int lua_engine_do_string(LuaEngine* engine, const char* code);
// 注册C函数
void lua_engine_register_function(LuaEngine* engine, const char* name, void* func);
// 获取状态
struct lua_State* lua_engine_get_state(LuaEngine* engine);
+289
View File
@@ -0,0 +1,289 @@
#include "terrain.h"
#include <stdlib.h>
#include <math.h>
#include <string.h>
// ============================================================================
// 简单的 Value Noise + 叠加
// ============================================================================
float hash_noise(int x, int z, int seed) {
unsigned int n = (unsigned int)x * 374761393u
+ (unsigned int)z * 668265263u
+ (unsigned int)seed * 1013904223u;
n = (n ^ (n >> 13)) * 1274126177u;
n = n ^ (n >> 16);
return (float)(n & 0x7fffffffu) / (float)0x7fffffffu;
}
static float smooth_noise(float x, float z, int seed) {
int ix = (int)floorf(x);
int iz = (int)floorf(z);
float fx = x - ix;
float fz = z - iz;
float v00 = hash_noise(ix, iz, seed);
float v10 = hash_noise(ix + 1, iz, seed);
float v01 = hash_noise(ix, iz + 1, seed);
float v11 = hash_noise(ix + 1, iz + 1, seed);
float u = fx * fx * (3.0f - 2.0f * fx);
float v = fz * fz * (3.0f - 2.0f * fz);
float a = v00 + (v10 - v00) * u;
float b = v01 + (v11 - v01) * u;
return a + (b - a) * v;
}
float fbm_noise(float x, float z, int seed, int octaves, float persistence) {
float total = 0.0f;
float amplitude = 1.0f;
float frequency = 1.0f;
float max_value = 0.0f;
for (int i = 0; i < octaves; i++) {
total += smooth_noise(x * frequency, z * frequency, seed + i * 57) * amplitude;
max_value += amplitude;
amplitude *= persistence;
frequency *= 2.0f;
}
return total / max_value;
}
// ============================================================================
// 高度图
// ============================================================================
TerrainConfig terrain_default_config(void) {
TerrainConfig cfg = {0};
cfg.chunks_x = 4;
cfg.chunks_z = 4;
cfg.vertices_per_chunk = 32;
cfg.chunk_size = 64.0f;
cfg.height_scale = 20.0f;
cfg.uv_scale = 0.1f;
return cfg;
}
TerrainHeightmap terrain_heightmap_create(int size) {
TerrainHeightmap hm = {0};
if (size < 2) size = 2;
if (size > TERRAIN_MAX_HEIGHTMAP_SIZE) size = TERRAIN_MAX_HEIGHTMAP_SIZE;
hm.size = size;
hm.data = (float*)calloc(size * size, sizeof(float));
return hm;
}
void terrain_heightmap_destroy(TerrainHeightmap* hm) {
if (hm && hm->data) {
free(hm->data);
hm->data = NULL;
hm->size = 0;
}
}
void terrain_heightmap_fill_noise(TerrainHeightmap* hm, int seed, float scale, float amplitude) {
if (!hm || !hm->data) return;
int s = hm->size;
for (int z = 0; z < s; z++) {
for (int x = 0; x < s; x++) {
float nx = (float)x / (float)(s - 1) * scale;
float nz = (float)z / (float)(s - 1) * scale;
float h = fbm_noise(nx, nz, seed, 6, 0.5f);
hm->data[z * s + x] = h * amplitude;
}
}
}
float terrain_heightmap_sample(const TerrainHeightmap* hm, float x, float z) {
if (!hm || !hm->data || hm->size < 2) return 0.0f;
int s = hm->size;
float fx = x * (float)(s - 1);
float fz = z * (float)(s - 1);
int ix = (int)floorf(fx);
int iz = (int)floorf(fz);
if (ix < 0) ix = 0;
if (iz < 0) iz = 0;
if (ix >= s - 1) ix = s - 2;
if (iz >= s - 1) iz = s - 2;
float tx = fx - ix;
float tz = fz - iz;
float h00 = hm->data[iz * s + ix];
float h10 = hm->data[iz * s + ix + 1];
float h01 = hm->data[(iz + 1) * s + ix];
float h11 = hm->data[(iz + 1) * s + ix + 1];
float a = h00 + (h10 - h00) * tx;
float b = h01 + (h11 - h01) * tx;
return a + (b - a) * tz;
}
// ============================================================================
// 地形网格
// ============================================================================
static Mesh* create_mesh_from_terrain(ID3D11Device* dev, const TerrainHeightmap* hm,
float size, float height_scale, float uv_scale) {
if (!hm || !hm->data || hm->size < 2) return NULL;
int s = hm->size;
int vc = s * s;
int ic = (s - 1) * (s - 1) * 6;
Vertex* verts = (Vertex*)calloc(vc, sizeof(Vertex));
uint32_t* idx = (uint32_t*)malloc(ic * sizeof(uint32_t));
float half = size * 0.5f;
float step = size / (float)(s - 1);
for (int z = 0; z < s; z++) {
for (int x = 0; x < s; x++) {
int i = z * s + x;
float px = (float)x * step - half;
float pz = (float)z * step - half;
float py = hm->data[i] * height_scale;
verts[i].pos[0] = px;
verts[i].pos[1] = py;
verts[i].pos[2] = pz;
verts[i].uv[0] = (float)x * uv_scale;
verts[i].uv[1] = (float)z * uv_scale;
verts[i].color[0] = 1.0f;
verts[i].color[1] = 1.0f;
verts[i].color[2] = 1.0f;
verts[i].color[3] = 1.0f;
}
}
// 计算法线
memset(verts, 0, vc * sizeof(Vertex)); // 重新初始化法线
for (int z = 0; z < s; z++) {
for (int x = 0; x < s; x++) {
int i = z * s + x;
float px = (float)x * step - half;
float pz = (float)z * step - half;
float py = hm->data[i] * height_scale;
verts[i].pos[0] = px;
verts[i].pos[1] = py;
verts[i].pos[2] = pz;
verts[i].uv[0] = (float)x * uv_scale;
verts[i].uv[1] = (float)z * uv_scale;
// 估算法线
float hl = (x > 0) ? hm->data[z * s + x - 1] * height_scale : py;
float hr = (x < s - 1) ? hm->data[z * s + x + 1] * height_scale : py;
float hd = (z > 0) ? hm->data[(z - 1) * s + x] * height_scale : py;
float hu = (z < s - 1) ? hm->data[(z + 1) * s + x] * height_scale : py;
verts[i].norm[0] = (hl - hr);
verts[i].norm[1] = 2.0f * step;
verts[i].norm[2] = (hd - hu);
float nx = verts[i].norm[0];
float ny = verts[i].norm[1];
float nz = verts[i].norm[2];
float len = sqrtf(nx * nx + ny * ny + nz * nz);
if (len > 0.0001f) {
verts[i].norm[0] = nx / len;
verts[i].norm[1] = ny / len;
verts[i].norm[2] = nz / len;
} else {
verts[i].norm[0] = 0;
verts[i].norm[1] = 1;
verts[i].norm[2] = 0;
}
// 根据高度和坡度着色
float h = hm->data[i];
float slope = (ny > 0.0001f) ? sqrtf(nx * nx + nz * nz) / ny : 0.0f;
if (h > 0.72f) {
verts[i].color[0] = 0.92f;
verts[i].color[1] = 0.92f;
verts[i].color[2] = 0.95f;
} else if (h > 0.55f || slope > 0.42f) {
verts[i].color[0] = 0.62f;
verts[i].color[1] = 0.60f;
verts[i].color[2] = 0.56f;
} else if (h < 0.18f) {
verts[i].color[0] = 0.82f;
verts[i].color[1] = 0.76f;
verts[i].color[2] = 0.50f;
} else {
verts[i].color[0] = 0.40f;
verts[i].color[1] = 0.68f;
verts[i].color[2] = 0.36f;
}
verts[i].color[3] = 1.0f;
}
}
int ii = 0;
for (int z = 0; z < s - 1; z++) {
for (int x = 0; x < s - 1; x++) {
uint32_t a = z * s + x;
uint32_t b = z * s + x + 1;
uint32_t c = (z + 1) * s + x;
uint32_t d = (z + 1) * s + x + 1;
idx[ii++] = a;
idx[ii++] = c;
idx[ii++] = b;
idx[ii++] = b;
idx[ii++] = c;
idx[ii++] = d;
}
}
Mesh* m = (Mesh*)calloc(1, sizeof(Mesh));
m->vcount = vc;
m->icount = ic;
m->stride = sizeof(Vertex);
D3D11_BUFFER_DESC bd = {0};
D3D11_SUBRESOURCE_DATA sd = {0};
bd.ByteWidth = sizeof(Vertex) * vc;
bd.Usage = D3D11_USAGE_DEFAULT;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
sd.pSysMem = verts;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->vb);
bd.ByteWidth = sizeof(uint32_t) * ic;
bd.BindFlags = D3D11_BIND_INDEX_BUFFER;
sd.pSysMem = idx;
dev->lpVtbl->CreateBuffer(dev, &bd, &sd, &m->ib);
free(verts);
free(idx);
return m;
}
Mesh* terrain_mesh_create(ID3D11Device* dev, const TerrainHeightmap* hm, float size, float height_scale, float uv_scale) {
return create_mesh_from_terrain(dev, hm, size, height_scale, uv_scale);
}
// ============================================================================
// 地形对象
// ============================================================================
Terrain* terrain_create(ID3D11Device* dev, const TerrainConfig* config) {
if (!dev || !config) return NULL;
Terrain* t = (Terrain*)calloc(1, sizeof(Terrain));
t->config = *config;
t->device = dev;
t->heightmap = terrain_heightmap_create(config->vertices_per_chunk);
terrain_heightmap_fill_noise(&t->heightmap, 12345, 4.0f, 1.0f);
t->mesh = terrain_mesh_create(dev, &t->heightmap, config->chunk_size, config->height_scale, config->uv_scale);
return t;
}
void terrain_destroy(Terrain* terrain) {
if (!terrain) return;
terrain_heightmap_destroy(&terrain->heightmap);
if (terrain->mesh) mesh_destroy(terrain->mesh);
free(terrain);
}
float terrain_get_height(const Terrain* terrain, float world_x, float world_z) {
if (!terrain) return 0.0f;
float half = terrain->config.chunk_size * 0.5f;
float local_x = (world_x + half) / terrain->config.chunk_size;
float local_z = (world_z + half) / terrain->config.chunk_size;
float h = terrain_heightmap_sample(&terrain->heightmap, local_x, local_z);
return h * terrain->config.height_scale;
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
// ============================================================================
// terrain.h - 程序化地形
// ============================================================================
#include <d3d11.h>
#include "../Rendering/mesh.h"
#define TERRAIN_MAX_HEIGHTMAP_SIZE 256
typedef struct {
float* data;
int size;
} TerrainHeightmap;
typedef struct {
int chunks_x;
int chunks_z;
int vertices_per_chunk;
float chunk_size;
float height_scale;
float uv_scale;
} TerrainConfig;
typedef struct {
TerrainConfig config;
TerrainHeightmap heightmap;
Mesh* mesh;
ID3D11Device* device;
} Terrain;
// 默认配置
TerrainConfig terrain_default_config(void);
// 高度图
TerrainHeightmap terrain_heightmap_create(int size);
void terrain_heightmap_destroy(TerrainHeightmap* hm);
void terrain_heightmap_fill_noise(TerrainHeightmap* hm, int seed, float scale, float amplitude);
float terrain_heightmap_sample(const TerrainHeightmap* hm, float x, float z);
// 地形对象
Terrain* terrain_create(ID3D11Device* dev, const TerrainConfig* config);
void terrain_destroy(Terrain* terrain);
// 从世界坐标采样高度 (用于相机/角色贴地)
float terrain_get_height(const Terrain* terrain, float world_x, float world_z);
// 网格生成
Mesh* terrain_mesh_create(ID3D11Device* dev, const TerrainHeightmap* hm, float size, float height_scale, float uv_scale);
// 噪声工具(供分块管理器复用)
float hash_noise(int x, int z, int seed);
float fbm_noise(float x, float z, int seed, int octaves, float persistence);
+199
View File
@@ -0,0 +1,199 @@
#include "terrain_manager.h"
#include "../Rendering/renderer.h"
#include "../Rendering/frustum.h"
#include "../Core/logger.h"
#include <stdlib.h>
#include <math.h>
// ============================================================================
// 噪声:分块需要世界一致的高度,这里复用 terrain.c 的 hash_noise/fbm_noise
// ============================================================================
static float sample_world_height(int seed, float world_x, float world_z, float scale) {
return fbm_noise(world_x * scale, world_z * scale, seed, 6, 0.5f);
}
static void fill_heightmap_for_chunk(TerrainHeightmap* hm, int seed, int cx, int cz,
float chunk_size, float scale) {
if (!hm || !hm->data) return;
int s = hm->size;
float half = chunk_size * 0.5f;
float step = chunk_size / (float)(s - 1);
float origin_x = (float)cx * chunk_size - half;
float origin_z = (float)cz * chunk_size - half;
for (int z = 0; z < s; z++) {
for (int x = 0; x < s; x++) {
float wx = origin_x + (float)x * step;
float wz = origin_z + (float)z * step;
hm->data[z * s + x] = sample_world_height(seed, wx, wz, scale);
}
}
}
static void make_chunk_world_matrix(TerrainChunk* chunk, Mat4* out) {
mat4_identity(out);
out->m[3][0] = (float)chunk->cx * chunk->terrain->config.chunk_size;
out->m[3][1] = 0.0f;
out->m[3][2] = (float)chunk->cz * chunk->terrain->config.chunk_size;
}
TerrainManager* terrain_manager_create(ID3D11Device* dev, const TerrainConfig* config, Material* base_material) {
if (!dev || !config) return NULL;
TerrainManager* tm = (TerrainManager*)calloc(1, sizeof(TerrainManager));
tm->config = *config;
tm->device = dev;
tm->base_material = base_material;
tm->load_radius = 3;
tm->seed = 12345;
tm->chunk_count = 0;
return tm;
}
void terrain_manager_destroy(TerrainManager* tm) {
if (!tm) return;
for (int i = 0; i < tm->chunk_count; i++) {
if (tm->chunks[i].terrain) terrain_destroy(tm->chunks[i].terrain);
}
free(tm);
}
static int find_chunk_index(TerrainManager* tm, int cx, int cz) {
for (int i = 0; i < tm->chunk_count; i++) {
if (tm->chunks[i].loaded && tm->chunks[i].cx == cx && tm->chunks[i].cz == cz)
return i;
}
return -1;
}
static TerrainChunk* alloc_chunk_slot(TerrainManager* tm) {
if (tm->chunk_count >= TERRAIN_MAX_CHUNKS) return NULL;
return &tm->chunks[tm->chunk_count++];
}
static TerrainChunk* create_chunk(TerrainManager* tm, int cx, int cz) {
TerrainChunk* chunk = alloc_chunk_slot(tm);
if (!chunk) {
LOG_ERROR("Terrain: failed to alloc chunk slot");
return NULL;
}
chunk->cx = cx;
chunk->cz = cz;
chunk->terrain = (Terrain*)calloc(1, sizeof(Terrain));
chunk->terrain->config = tm->config;
chunk->terrain->device = tm->device;
chunk->terrain->heightmap = terrain_heightmap_create(tm->config.vertices_per_chunk);
fill_heightmap_for_chunk(&chunk->terrain->heightmap, tm->seed, cx, cz,
tm->config.chunk_size, 0.015f);
chunk->terrain->mesh = terrain_mesh_create(tm->device, &chunk->terrain->heightmap,
tm->config.chunk_size,
tm->config.height_scale,
tm->config.uv_scale);
chunk->material = tm->base_material;
chunk->loaded = 1;
if (!chunk->terrain->mesh) {
LOG_ERROR("Terrain: chunk mesh create failed at (%d, %d)", cx, cz);
} else {
LOG_INFO("Terrain: created chunk (%d, %d) mesh v=%u i=%u", cx, cz,
chunk->terrain->mesh->vcount, chunk->terrain->mesh->icount);
}
return chunk;
}
void terrain_manager_update(TerrainManager* tm, Vec3 observer_pos) {
if (!tm) return;
float inv_size = 1.0f / tm->config.chunk_size;
int ox = (int)floorf(observer_pos.x * inv_size + 0.5f);
int oz = (int)floorf(observer_pos.z * inv_size + 0.5f);
static int logged = 0;
if (!logged) {
LOG_INFO("Terrain update: observer=(%.1f,%.1f,%.1f) center_chunk=(%d,%d)",
observer_pos.x, observer_pos.y, observer_pos.z, ox, oz);
logged = 1;
}
// 标记需要保留
int keep[TERRAIN_MAX_CHUNKS] = {0};
// 加载需要的分块
int r = tm->load_radius;
for (int z = oz - r; z <= oz + r; z++) {
for (int x = ox - r; x <= ox + r; x++) {
int idx = find_chunk_index(tm, x, z);
if (idx < 0) {
if (create_chunk(tm, x, z)) {
keep[tm->chunk_count - 1] = 1;
}
} else {
keep[idx] = 1;
}
}
}
// 卸载不需要的分块
int write = 0;
for (int i = 0; i < tm->chunk_count; i++) {
if (keep[i]) {
tm->chunks[write] = tm->chunks[i];
write++;
} else {
terrain_destroy(tm->chunks[i].terrain);
}
}
tm->chunk_count = write;
}
void terrain_manager_render(TerrainManager* tm, Renderer* renderer) {
if (!tm || !renderer) return;
static int logged = 0;
if (!logged) {
LOG_INFO("Terrain render: chunk_count=%d", tm->chunk_count);
logged = 1;
}
Mat4 vp;
renderer_get_view_projection(renderer, &vp);
Vec4 planes[6];
frustum_from_matrix(&vp, planes);
float half = tm->config.chunk_size * 0.5f;
float radius = sqrtf(half * half * 2.0f +
(tm->config.height_scale * 0.5f) *
(tm->config.height_scale * 0.5f));
float cy = tm->config.height_scale * 0.5f;
int rendered = 0;
for (int i = 0; i < tm->chunk_count; i++) {
TerrainChunk* c = &tm->chunks[i];
if (!c->loaded || !c->terrain || !c->terrain->mesh) continue;
Vec3 center = { (float)c->cx * tm->config.chunk_size, cy, (float)c->cz * tm->config.chunk_size };
if (!frustum_intersects_sphere(planes, center, radius)) continue;
Mat4 world;
make_chunk_world_matrix(c, &world);
renderer_draw_mesh_with_material(renderer, c->terrain->mesh, c->material, &world);
rendered++;
}
LOG_INFO("Terrain render: visible_chunks=%d/%d", rendered, tm->chunk_count);
}
float terrain_manager_get_height(TerrainManager* tm, float world_x, float world_z) {
if (!tm) return 0.0f;
float inv_size = 1.0f / tm->config.chunk_size;
int cx = (int)floorf(world_x * inv_size + 0.5f);
int cz = (int)floorf(world_z * inv_size + 0.5f);
// 先在已加载分块中查找
int idx = find_chunk_index(tm, cx, cz);
if (idx >= 0 && tm->chunks[idx].terrain) {
Terrain* t = tm->chunks[idx].terrain;
float local_x = world_x - (float)cx * tm->config.chunk_size;
float local_z = world_z - (float)cz * tm->config.chunk_size;
return terrain_get_height(t, local_x, local_z);
}
// 回退到直接采样噪声
return sample_world_height(tm->seed, world_x, world_z, 0.015f) * tm->config.height_scale;
}
+42
View File
@@ -0,0 +1,42 @@
#pragma once
// ============================================================================
// terrain_manager.h - 地形分块流式加载
// ============================================================================
#include "terrain.h"
#include "../Rendering/material.h"
#include "../Rendering/renderer.h"
#include "../ASM/math_c_fallback.h"
#define TERRAIN_MAX_CHUNKS 64
typedef struct {
int cx;
int cz;
Terrain* terrain;
Material* material;
int loaded;
} TerrainChunk;
typedef struct {
TerrainConfig config;
TerrainChunk chunks[TERRAIN_MAX_CHUNKS];
int chunk_count;
int load_radius;
int seed;
ID3D11Device* device;
Material* base_material;
} TerrainManager;
TerrainManager* terrain_manager_create(ID3D11Device* dev, const TerrainConfig* config, Material* base_material);
void terrain_manager_destroy(TerrainManager* tm);
// 根据观察者位置更新分块
void terrain_manager_update(TerrainManager* tm, Vec3 observer_pos);
// 渲染所有已加载分块
void terrain_manager_render(TerrainManager* tm, Renderer* renderer);
// 从世界坐标获取地形高度(在所有已加载分块中采样)
float terrain_manager_get_height(TerrainManager* tm, float world_x, float world_z);
+401
View File
@@ -0,0 +1,401 @@
#include "ui.h"
#include <stdlib.h>
#include <string.h>
#include <d3dcompiler.h>
#include <windows.h>
// 为 MinGW 提供 COM 助手宏(MSVC 已内联提供)
#ifndef _MSC_VER
#define ID3D11Device_CreateBuffer(p,a,b,c) (p)->lpVtbl->CreateBuffer(p,a,b,c)
#define ID3D11Device_CreateVertexShader(p,a,b,c,d) (p)->lpVtbl->CreateVertexShader(p,a,b,c,d)
#define ID3D11Device_CreatePixelShader(p,a,b,c,d) (p)->lpVtbl->CreatePixelShader(p,a,b,c,d)
#define ID3D11Device_CreateInputLayout(p,a,b,c,d,e) (p)->lpVtbl->CreateInputLayout(p,a,b,c,d,e)
#define ID3D11Device_CreateBlendState(p,a,b) (p)->lpVtbl->CreateBlendState(p,a,b)
#define ID3D11Device_CreateRasterizerState(p,a,b) (p)->lpVtbl->CreateRasterizerState(p,a,b)
#define ID3D11Device_CreateTexture2D(p,a,b,c) (p)->lpVtbl->CreateTexture2D(p,a,b,c)
#define ID3D11Device_CreateShaderResourceView(p,a,b,c) (p)->lpVtbl->CreateShaderResourceView(p,a,b,c)
#define ID3D11Device_CreateSamplerState(p,a,b) (p)->lpVtbl->CreateSamplerState(p,a,b)
#define ID3D11RasterizerState_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11BlendState_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11InputLayout_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11PixelShader_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11VertexShader_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11Buffer_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11Texture2D_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11ShaderResourceView_Release(p) (p)->lpVtbl->Release(p)
#define ID3D11SamplerState_Release(p) (p)->lpVtbl->Release(p)
#define ID3D10Blob_Release(p) (p)->lpVtbl->Release(p)
#define ID3D10Blob_GetBufferPointer(p) (p)->lpVtbl->GetBufferPointer(p)
#define ID3D10Blob_GetBufferSize(p) (p)->lpVtbl->GetBufferSize(p)
#define ID3D11DeviceContext_Map(p,a,b,c,d,e) (p)->lpVtbl->Map(p,a,b,c,d,e)
#define ID3D11DeviceContext_Unmap(p,a,b) (p)->lpVtbl->Unmap(p,a,b)
#define ID3D11DeviceContext_IASetVertexBuffers(p,a,b,c,d,e) (p)->lpVtbl->IASetVertexBuffers(p,a,b,c,d,e)
#define ID3D11DeviceContext_IASetIndexBuffer(p,a,b,c) (p)->lpVtbl->IASetIndexBuffer(p,a,b,c)
#define ID3D11DeviceContext_IASetPrimitiveTopology(p,a) (p)->lpVtbl->IASetPrimitiveTopology(p,a)
#define ID3D11DeviceContext_IASetInputLayout(p,a) (p)->lpVtbl->IASetInputLayout(p,a)
#define ID3D11DeviceContext_VSSetShader(p,a,b,c) (p)->lpVtbl->VSSetShader(p,a,b,c)
#define ID3D11DeviceContext_PSSetShader(p,a,b,c) (p)->lpVtbl->PSSetShader(p,a,b,c)
#define ID3D11DeviceContext_PSSetShaderResources(p,a,b,c) (p)->lpVtbl->PSSetShaderResources(p,a,b,c)
#define ID3D11DeviceContext_PSSetSamplers(p,a,b,c) (p)->lpVtbl->PSSetSamplers(p,a,b,c)
#define ID3D11DeviceContext_OMSetBlendState(p,a,b,c) (p)->lpVtbl->OMSetBlendState(p,a,b,c)
#define ID3D11DeviceContext_RSSetState(p,a) (p)->lpVtbl->RSSetState(p,a)
#define ID3D11DeviceContext_DrawIndexed(p,a,b,c) (p)->lpVtbl->DrawIndexed(p,a,b,c)
#endif
static const char* ui_vs = R"(
struct VS_INPUT { float2 Pos : POSITION; float2 UV : TEXCOORD0; uint Color : COLOR0; };
struct VS_OUTPUT { float4 Pos : SV_POSITION; float2 UV : TEXCOORD0; float4 Color : COLOR0; };
VS_OUTPUT main(VS_INPUT input) {
VS_OUTPUT output;
output.Pos = float4(input.Pos.x, input.Pos.y, 0, 1);
output.UV = input.UV;
output.Color = float4((input.Color & 0xFF) / 255.0, ((input.Color >> 8) & 0xFF) / 255.0,
((input.Color >> 16) & 0xFF) / 255.0, ((input.Color >> 24) & 0xFF) / 255.0);
return output;
}
)";
static const char* ui_ps = R"(
struct PS_INPUT { float4 Pos : SV_POSITION; float2 UV : TEXCOORD0; float4 Color : COLOR0; };
Texture2D tex : register(t0);
SamplerState samp : register(s0);
float4 main(PS_INPUT input) : SV_TARGET { return input.Color * tex.Sample(samp, input.UV); }
)";
#define UI_FONT_COLS 16
#define UI_FONT_ROWS 8
#define UI_FONT_CELL_W 16
#define UI_FONT_CELL_H 16
#define UI_FONT_ATLAS_W (UI_FONT_COLS * UI_FONT_CELL_W)
#define UI_FONT_ATLAS_H (UI_FONT_ROWS * UI_FONT_CELL_H)
static int ui_create_font_texture(UI* ui, ID3D11Device* device) {
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = UI_FONT_ATLAS_W;
bmi.bmiHeader.biHeight = -UI_FONT_ATLAS_H;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
HDC hdc = GetDC(NULL);
HDC memdc = CreateCompatibleDC(hdc);
void* bits = NULL;
HBITMAP bmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
if (!bmp) {
DeleteDC(memdc);
ReleaseDC(NULL, hdc);
return 0;
}
SelectObject(memdc, bmp);
PatBlt(memdc, 0, 0, UI_FONT_ATLAS_W, UI_FONT_ATLAS_H, BLACKNESS);
SetBkColor(memdc, RGB(0, 0, 0));
SetTextColor(memdc, RGB(255, 255, 255));
HFONT font = CreateFont(UI_FONT_CELL_H - 2, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, FIXED_PITCH | FF_MODERN, "Consolas");
if (font) SelectObject(memdc, font);
for (int i = 32; i < 128; i++) {
int col = i % UI_FONT_COLS;
int row = i / UI_FONT_COLS;
char c = (char)i;
TextOutA(memdc, col * UI_FONT_CELL_W + 1, row * UI_FONT_CELL_H + 1, &c, 1);
ui->char_uvs[i][0] = (float)(col * UI_FONT_CELL_W) / UI_FONT_ATLAS_W;
ui->char_uvs[i][1] = (float)(row * UI_FONT_CELL_H) / UI_FONT_ATLAS_H;
ui->char_uvs[i][2] = (float)((col + 1) * UI_FONT_CELL_W) / UI_FONT_ATLAS_W;
ui->char_uvs[i][3] = (float)((row + 1) * UI_FONT_CELL_H) / UI_FONT_ATLAS_H;
}
// 将亮度转为 alpha,文字存储为白色
uint32_t* pixels = (uint32_t*)bits;
for (int i = 0; i < UI_FONT_ATLAS_W * UI_FONT_ATLAS_H; i++) {
uint32_t p = pixels[i];
uint8_t b = (uint8_t)(p & 0xFF);
uint8_t g = (uint8_t)((p >> 8) & 0xFF);
uint8_t r = (uint8_t)((p >> 16) & 0xFF);
uint8_t lum = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8);
pixels[i] = 0x00FFFFFF | ((uint32_t)lum << 24);
}
// 左上角像素保留为纯白,供纯色矩形采样
pixels[0] = 0xFFFFFFFF;
D3D11_TEXTURE2D_DESC desc = {0};
desc.Width = UI_FONT_ATLAS_W;
desc.Height = UI_FONT_ATLAS_H;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_IMMUTABLE;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
D3D11_SUBRESOURCE_DATA init = {0};
init.pSysMem = bits;
init.SysMemPitch = UI_FONT_ATLAS_W * 4;
ID3D11Texture2D* tex = NULL;
HRESULT hr = ID3D11Device_CreateTexture2D(device, &desc, &init, &tex);
if (SUCCEEDED(hr) && tex) {
ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource*)tex, NULL, &ui->font_srv);
ID3D11Texture2D_Release(tex);
}
D3D11_SAMPLER_DESC sd = {0};
sd.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
sd.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sd.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
sd.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
ID3D11Device_CreateSamplerState(device, &sd, &ui->font_sampler);
if (font) DeleteObject(font);
DeleteObject(bmp);
DeleteDC(memdc);
ReleaseDC(NULL, hdc);
return ui->font_srv != NULL && ui->font_sampler != NULL;
}
UI* ui_create(ID3D11Device* device) {
UI* ui = (UI*)calloc(1, sizeof(UI));
ui->font_size = 14.0f;
ui->text_color = 0xFFFFFFFF;
ui->bg_color = 0xFF333333;
ui->border_color = 0xFF666666;
ui->padding = 4.0f;
D3D11_BUFFER_DESC vbd = {0};
vbd.ByteWidth = sizeof(UIVertex) * UI_MAX_VERTICES;
vbd.Usage = D3D11_USAGE_DYNAMIC;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
ID3D11Device_CreateBuffer(device, &vbd, NULL, &ui->vertex_buffer);
D3D11_BUFFER_DESC ibd = {0};
ibd.ByteWidth = sizeof(uint32_t) * UI_MAX_INDICES;
ibd.Usage = D3D11_USAGE_DYNAMIC;
ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
ibd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
ID3D11Device_CreateBuffer(device, &ibd, NULL, &ui->index_buffer);
// 编译着色器
ID3DBlob* vs_blob = NULL, *ps_blob = NULL, *err = NULL;
D3DCompile(ui_vs, strlen(ui_vs), NULL, NULL, NULL, "main", "vs_5_0", 0, 0, &vs_blob, &err);
D3DCompile(ui_ps, strlen(ui_ps), NULL, NULL, NULL, "main", "ps_5_0", 0, 0, &ps_blob, &err);
if (vs_blob && ps_blob) {
ID3D11Device_CreateVertexShader(device, ID3D10Blob_GetBufferPointer(vs_blob),
ID3D10Blob_GetBufferSize(vs_blob), NULL, &ui->vertex_shader);
ID3D11Device_CreatePixelShader(device, ID3D10Blob_GetBufferPointer(ps_blob),
ID3D10Blob_GetBufferSize(ps_blob), NULL, &ui->pixel_shader);
D3D11_INPUT_ELEMENT_DESC layout[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 16, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
ID3D11Device_CreateInputLayout(device, layout, 3,
ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &ui->input_layout);
ID3D10Blob_Release(vs_blob);
ID3D10Blob_Release(ps_blob);
}
D3D11_BLEND_DESC bd = {0};
bd.RenderTarget[0].BlendEnable = TRUE;
bd.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
bd.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
bd.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
bd.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
bd.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
bd.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
ID3D11Device_CreateBlendState(device, &bd, &ui->blend_state);
D3D11_RASTERIZER_DESC rd = {0};
rd.FillMode = D3D11_FILL_SOLID;
rd.CullMode = D3D11_CULL_NONE;
rd.ScissorEnable = TRUE;
ID3D11Device_CreateRasterizerState(device, &rd, &ui->rasterizer);
ui_create_font_texture(ui, device);
return ui;
}
void ui_destroy(UI* ui) {
if (!ui) return;
if (ui->font_sampler) ID3D11SamplerState_Release(ui->font_sampler);
if (ui->font_srv) ID3D11ShaderResourceView_Release(ui->font_srv);
if (ui->rasterizer) ID3D11RasterizerState_Release(ui->rasterizer);
if (ui->blend_state) ID3D11BlendState_Release(ui->blend_state);
if (ui->input_layout) ID3D11InputLayout_Release(ui->input_layout);
if (ui->pixel_shader) ID3D11PixelShader_Release(ui->pixel_shader);
if (ui->vertex_shader) ID3D11VertexShader_Release(ui->vertex_shader);
if (ui->index_buffer) ID3D11Buffer_Release(ui->index_buffer);
if (ui->vertex_buffer) ID3D11Buffer_Release(ui->vertex_buffer);
free(ui);
}
void ui_begin(UI* ui, float mx, float my, int md, int mp, int mr) {
ui->mouse_x = mx;
ui->mouse_y = my;
ui->mouse_down = md;
ui->mouse_pressed = mp;
ui->mouse_released = mr;
ui->vertex_count = 0;
ui->index_count = 0;
}
void ui_end(UI* ui) {
(void)ui;
}
void ui_draw_rect(UI* ui, float x, float y, float w, float h, uint32_t color) {
uint32_t base = ui->vertex_count;
// UV 指向字体图集左上角纯白像素,使纯色矩形不受纹理内容影响
ui->vertices[ui->vertex_count++] = (UIVertex){x, y, 0.0f, 0.0f, color};
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y, 0.0f, 0.0f, color};
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y + h, 0.0f, 0.0f, color};
ui->vertices[ui->vertex_count++] = (UIVertex){x, y + h, 0.0f, 0.0f, color};
ui->indices[ui->index_count++] = base;
ui->indices[ui->index_count++] = base + 1;
ui->indices[ui->index_count++] = base + 2;
ui->indices[ui->index_count++] = base;
ui->indices[ui->index_count++] = base + 2;
ui->indices[ui->index_count++] = base + 3;
}
void ui_draw_text(UI* ui, float x, float y, const char* text, uint32_t color) {
if (!text) return;
float px = x;
float py = y;
const float cw = (float)UI_FONT_CELL_W;
const float ch = (float)UI_FONT_CELL_H;
while (*text) {
unsigned char c = (unsigned char)*text++;
if (c < 32 || c >= 128) c = '?';
float u1 = ui->char_uvs[c][0];
float v1 = ui->char_uvs[c][1];
float u2 = ui->char_uvs[c][2];
float v2 = ui->char_uvs[c][3];
uint32_t base = ui->vertex_count;
ui->vertices[ui->vertex_count++] = (UIVertex){px, py, u1, v1, color};
ui->vertices[ui->vertex_count++] = (UIVertex){px + cw, py, u2, v1, color};
ui->vertices[ui->vertex_count++] = (UIVertex){px + cw, py + ch, u2, v2, color};
ui->vertices[ui->vertex_count++] = (UIVertex){px, py + ch, u1, v2, color};
ui->indices[ui->index_count++] = base;
ui->indices[ui->index_count++] = base + 1;
ui->indices[ui->index_count++] = base + 2;
ui->indices[ui->index_count++] = base;
ui->indices[ui->index_count++] = base + 2;
ui->indices[ui->index_count++] = base + 3;
px += cw;
}
}
void ui_draw_rect_border(UI* ui, float x, float y, float w, float h,
uint32_t fill, uint32_t border, float thickness) {
ui_draw_rect(ui, x, y, w, h, fill);
ui_draw_rect(ui, x, y, w, thickness, border);
ui_draw_rect(ui, x, y + h - thickness, w, thickness, border);
ui_draw_rect(ui, x, y, thickness, h, border);
ui_draw_rect(ui, x + w - thickness, y, thickness, h, border);
}
int ui_button(UI* ui, float x, float y, float w, float h, const char* text) {
int hover = ui->mouse_x >= x && ui->mouse_x <= x + w &&
ui->mouse_y >= y && ui->mouse_y <= y + h;
uint32_t color = hover ? 0xFF555555 : 0xFF444444;
if (hover && ui->mouse_pressed) color = 0xFF666666;
ui_draw_rect_border(ui, x, y, w, h, color, 0xFF888888, 1);
if (text) {
float tx = x + ui->padding;
float ty = y + (h - UI_FONT_CELL_H) * 0.5f;
ui_draw_text(ui, tx, ty, text, ui->text_color);
}
return hover && ui->mouse_pressed;
}
float ui_slider(UI* ui, float x, float y, float w, float h, float value, float min_val, float max_val) {
ui_draw_rect_border(ui, x, y, w, h, 0xFF333333, 0xFF666666, 1);
int hover = ui->mouse_x >= x && ui->mouse_x <= x + w &&
ui->mouse_y >= y && ui->mouse_y <= y + h;
if (hover && ui->mouse_down) {
float t = (ui->mouse_x - x) / w;
value = min_val + t * (max_val - min_val);
if (value < min_val) value = min_val;
if (value > max_val) value = max_val;
}
float knob_x = x + ((value - min_val) / (max_val - min_val)) * (w - h);
ui_draw_rect(ui, knob_x, y, h, h, 0xFF0078D4);
return value;
}
int ui_checkbox(UI* ui, float x, float y, const char* text, int checked) {
float s = 16.0f;
ui_draw_rect_border(ui, x, y, s, s, checked ? 0xFF0078D4 : 0xFF333333, 0xFF666666, 1);
if (checked) {
// 绘制对勾
ui_draw_rect(ui, x + 4, y + 7, 2, 2, 0xFFFFFFFF);
ui_draw_rect(ui, x + 6, y + 9, 2, 2, 0xFFFFFFFF);
ui_draw_rect(ui, x + 8, y + 5, 5, 2, 0xFFFFFFFF);
}
if (text) {
ui_draw_text(ui, x + s + ui->padding, y + (s - UI_FONT_CELL_H) * 0.5f, text, ui->text_color);
}
if (ui->mouse_x >= x && ui->mouse_x <= x + s &&
ui->mouse_y >= y && ui->mouse_y <= y + s && ui->mouse_pressed) {
return !checked;
}
return checked;
}
void ui_label(UI* ui, float x, float y, const char* text) {
if (text) {
ui_draw_text(ui, x, y, text, ui->text_color);
}
}
void ui_render(UI* ui, ID3D11DeviceContext* ctx) {
if (ui->vertex_count == 0) return;
D3D11_MAPPED_SUBRESOURCE mapped;
if (SUCCEEDED(ID3D11DeviceContext_Map(ctx, (ID3D11Resource*)ui->vertex_buffer, 0,
D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
memcpy(mapped.pData, ui->vertices, sizeof(UIVertex) * ui->vertex_count);
ID3D11DeviceContext_Unmap(ctx, (ID3D11Resource*)ui->vertex_buffer, 0);
}
if (SUCCEEDED(ID3D11DeviceContext_Map(ctx, (ID3D11Resource*)ui->index_buffer, 0,
D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
memcpy(mapped.pData, ui->indices, sizeof(uint32_t) * ui->index_count);
ID3D11DeviceContext_Unmap(ctx, (ID3D11Resource*)ui->index_buffer, 0);
}
UINT stride = sizeof(UIVertex);
UINT offset = 0;
ID3D11DeviceContext_IASetVertexBuffers(ctx, 0, 1, &ui->vertex_buffer, &stride, &offset);
ID3D11DeviceContext_IASetIndexBuffer(ctx, ui->index_buffer, DXGI_FORMAT_R32_UINT, 0);
ID3D11DeviceContext_IASetPrimitiveTopology(ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D11DeviceContext_IASetInputLayout(ctx, ui->input_layout);
ID3D11DeviceContext_VSSetShader(ctx, ui->vertex_shader, NULL, 0);
ID3D11DeviceContext_PSSetShader(ctx, ui->pixel_shader, NULL, 0);
ID3D11DeviceContext_PSSetShaderResources(ctx, 0, 1, &ui->font_srv);
ID3D11DeviceContext_PSSetSamplers(ctx, 0, 1, &ui->font_sampler);
float blend[4] = {1, 1, 1, 1};
ID3D11DeviceContext_OMSetBlendState(ctx, ui->blend_state, blend, 0xFFFFFFFF);
ID3D11DeviceContext_RSSetState(ctx, ui->rasterizer);
ID3D11DeviceContext_DrawIndexed(ctx, ui->index_count, 0, 0);
ID3D11DeviceContext_OMSetBlendState(ctx, NULL, blend, 0xFFFFFFFF);
ID3D11DeviceContext_RSSetState(ctx, NULL);
}
+74
View File
@@ -0,0 +1,74 @@
#pragma once
// ============================================================================
// ui.h - 立即模式UI
// ============================================================================
#include <d3d11.h>
#include <stdint.h>
#define UI_MAX_VERTICES 16384
#define UI_MAX_INDICES 32768
#define UI_MAX_WIDGETS 256
typedef struct {
float x, y;
float u, v;
uint32_t color;
} UIVertex;
typedef struct {
float x, y, w, h;
uint32_t color;
int visible;
} UIRect;
typedef struct {
ID3D11Buffer* vertex_buffer;
ID3D11Buffer* index_buffer;
ID3D11VertexShader* vertex_shader;
ID3D11PixelShader* pixel_shader;
ID3D11InputLayout* input_layout;
ID3D11BlendState* blend_state;
ID3D11RasterizerState* rasterizer;
ID3D11ShaderResourceView* font_srv;
ID3D11SamplerState* font_sampler;
UIVertex vertices[UI_MAX_VERTICES];
uint32_t indices[UI_MAX_INDICES];
uint32_t vertex_count;
uint32_t index_count;
float mouse_x, mouse_y;
int mouse_down;
int mouse_pressed;
int mouse_released;
float font_size;
uint32_t text_color;
uint32_t bg_color;
uint32_t border_color;
float padding;
float char_uvs[128][4];
} UI;
// 创建/销毁
UI* ui_create(ID3D11Device* device);
void ui_destroy(UI* ui);
// 帧操作
void ui_begin(UI* ui, float mouse_x, float mouse_y, int mouse_down, int mouse_pressed, int mouse_released);
void ui_end(UI* ui);
void ui_render(UI* ui, ID3D11DeviceContext* ctx);
// 基础绘制
void ui_draw_rect(UI* ui, float x, float y, float w, float h, uint32_t color);
void ui_draw_rect_border(UI* ui, float x, float y, float w, float h, uint32_t fill, uint32_t border, float thickness);
void ui_draw_text(UI* ui, float x, float y, const char* text, uint32_t color);
// 控件
int ui_button(UI* ui, float x, float y, float w, float h, const char* text);
float ui_slider(UI* ui, float x, float y, float w, float h, float value, float min_val, float max_val);
int ui_checkbox(UI* ui, float x, float y, const char* text, int checked);
void ui_label(UI* ui, float x, float y, const char* text);
+159
View File
@@ -0,0 +1,159 @@
// ============================================================================
// block_core.c - 积木核心操作
// ============================================================================
#include "block_core.h"
#include <stdlib.h>
#include <string.h>
// 创建积木
Block* block_create(BlockType type, float x, float y) {
Block* block = (Block*)calloc(1, sizeof(Block));
if (!block) return NULL;
block->type = type;
block->x = x;
block->y = y;
block->width = 200;
block->height = 60;
// 初始化槽位
block->input_count = 0;
block->output_count = 0;
for (int i = 0; i < MAX_BLOCK_INPUTS; i++) {
block->inputs[i].owner = block;
block->inputs[i].id = i;
}
for (int i = 0; i < MAX_BLOCK_OUTPUTS; i++) {
block->outputs[i].owner = block;
block->outputs[i].id = i;
}
return block;
}
// 复制积木 (深拷贝,不复制连接关系)
Block* block_copy(const Block* src) {
if (!src) return NULL;
Block* block = (Block*)malloc(sizeof(Block));
if (!block) return NULL;
memcpy(block, src, sizeof(Block));
// 重新设置拥有者,断开所有连接
for (int i = 0; i < MAX_BLOCK_INPUTS; i++) {
block->inputs[i].owner = block;
block->inputs[i].connected = NULL;
}
for (int i = 0; i < MAX_BLOCK_OUTPUTS; i++) {
block->outputs[i].owner = block;
block->outputs[i].connected = NULL;
}
block->next = NULL;
block->parent_if = NULL;
block->workspace = NULL;
block->user_data = NULL;
return block;
}
// 销毁积木
void block_destroy(Block* block) {
if (!block) return;
free(block);
}
// 添加输入槽位
BlockSlot* block_add_input(Block* block, SlotKind kind, SlotValueType value_type, const char* label) {
if (block->input_count >= MAX_BLOCK_INPUTS) return NULL;
BlockSlot* slot = &block->inputs[block->input_count];
slot->kind = kind;
slot->value_type = value_type;
if (label) strncpy(slot->label, label, MAX_BLOCK_LABEL - 1);
slot->value.type = value_type;
block->input_count++;
return slot;
}
// 添加输出槽位
BlockSlot* block_add_output(Block* block, SlotKind kind, SlotValueType value_type, const char* label) {
if (block->output_count >= MAX_BLOCK_OUTPUTS) return NULL;
BlockSlot* slot = &block->outputs[block->output_count];
slot->kind = kind;
slot->value_type = value_type;
if (label) strncpy(slot->label, label, MAX_BLOCK_LABEL - 1);
block->output_count++;
return slot;
}
// 连接两个槽位
bool block_connect(BlockSlot* from, BlockSlot* to) {
if (!from || !to) return false;
if (from->owner == to->owner) return false; // 不能自连
// 断开旧连接
if (from->connected) from->connected->connected = NULL;
if (to->connected) to->connected->connected = NULL;
from->connected = to;
to->connected = from;
return true;
}
// 断开槽位
void block_disconnect(BlockSlot* slot) {
if (!slot || !slot->connected) return;
slot->connected->connected = NULL;
slot->connected = NULL;
}
// 检查槽位是否已连接
bool block_slot_connected(BlockSlot* slot) {
return slot && slot->connected != NULL;
}
// 获取槽位的值
SlotValue block_slot_get_value(BlockSlot* slot) {
if (!slot) {
SlotValue empty = {0};
return empty;
}
// 如果有连接,从连接的积木获取值
if (slot->connected && slot->connected->owner) {
Block* source = slot->connected->owner;
// 从源积木的输出槽位获取值
for (int i = 0; i < source->output_count; i++) {
if (&source->outputs[i] == slot->connected) {
return source->outputs[i].value;
}
}
}
return slot->value;
}
// 设置槽位的值
void block_slot_set_value(BlockSlot* slot, SlotValue value) {
if (slot) slot->value = value;
}
// 积木是否包含点
bool block_contains_point(Block* block, float px, float py) {
return px >= block->x && px <= block->x + block->width
&& py >= block->y && py <= block->y + block->height;
}
// 获取槽位的世界位置
void block_slot_world_pos(BlockSlot* slot, float* wx, float* wy) {
if (!slot || !slot->owner) {
*wx = 0; *wy = 0;
return;
}
*wx = slot->owner->x + slot->x;
*wy = slot->owner->y + slot->y;
}
+183
View File
@@ -0,0 +1,183 @@
#pragma once
// ============================================================================
// block_core.h - 积木核心数据结构
// ============================================================================
#include <stdint.h>
#include <stdbool.h>
#define MAX_BLOCK_INPUTS 8
#define MAX_BLOCK_OUTPUTS 8
#define MAX_BLOCK_LABEL 64
// 颜色
typedef struct {
float r, g, b, a;
} Color;
typedef Color BlockColor;
// 预定义颜色
#define BLOCK_COLOR_EVENT {0.9f, 0.2f, 0.2f, 1.0f} // 红
#define BLOCK_COLOR_ACTION {0.2f, 0.5f, 0.9f, 1.0f} // 蓝
#define BLOCK_COLOR_CONTROL {0.2f, 0.7f, 0.3f, 1.0f} // 绿
#define BLOCK_COLOR_OPERATOR {0.9f, 0.6f, 0.1f, 1.0f} // 橙
#define BLOCK_COLOR_VARIABLE {0.7f, 0.3f, 0.8f, 1.0f} // 紫
#define BLOCK_COLOR_FUNCTION {0.9f, 0.8f, 0.2f, 1.0f} // 黄
#define BLOCK_COLOR_DATA {0.4f, 0.7f, 0.9f, 1.0f} // 浅蓝
// 槽位值类型
typedef enum {
SLOT_VALUE_NUMBER = 0,
SLOT_VALUE_STRING,
SLOT_VALUE_BOOLEAN,
SLOT_VALUE_VECTOR3,
SLOT_VALUE_ANY
} SlotValueType;
// 槽位值
typedef struct {
SlotValueType type;
union {
double number;
char string[256];
int boolean;
struct { float x, y, z; } vector3;
} as;
} SlotValue;
// 槽位类型
typedef enum {
SLOT_STATEMENT = 0, // 语句槽位 (执行流)
SLOT_VALUE // 值槽位 (数据流)
} SlotKind;
// 槽位
typedef struct BlockSlot {
int id;
SlotKind kind;
SlotValueType value_type;
char label[MAX_BLOCK_LABEL];
struct Block* owner; // 所属积木
struct BlockSlot* connected; // 连接的槽位
SlotValue value; // 当前值 (仅值槽位)
float x, y; // 相对于积木的位置
float width, height; // 槽位尺寸
} BlockSlot;
// 积木类型
typedef enum {
// 事件
BLOCK_EVENT_START = 0,
BLOCK_EVENT_UPDATE,
BLOCK_EVENT_COLLISION,
BLOCK_EVENT_KEY_PRESSED,
BLOCK_EVENT_MOUSE_CLICK,
BLOCK_EVENT_TIMER,
// 动作
BLOCK_ACTION_MOVE,
BLOCK_ACTION_ROTATE,
BLOCK_ACTION_SCALE,
BLOCK_ACTION_SET_POSITION,
BLOCK_ACTION_SET_ROTATION,
BLOCK_ACTION_PLAY_SOUND,
BLOCK_ACTION_DESTROY,
BLOCK_ACTION_INSTANTIATE,
// 控制
BLOCK_CONTROL_IF,
BLOCK_CONTROL_IF_ELSE,
BLOCK_CONTROL_REPEAT,
BLOCK_CONTROL_REPEAT_TIMES,
BLOCK_CONTROL_WHILE,
BLOCK_CONTROL_WAIT,
BLOCK_CONTROL_FUNCTION_DEF,
// 运算
BLOCK_OP_ADD,
BLOCK_OP_SUB,
BLOCK_OP_MUL,
BLOCK_OP_DIV,
BLOCK_OP_MOD,
BLOCK_OP_EQUALS,
BLOCK_OP_NOT_EQUALS,
BLOCK_OP_GREATER,
BLOCK_OP_LESS,
BLOCK_OP_AND,
BLOCK_OP_OR,
BLOCK_OP_NOT,
// 变量
BLOCK_VAR_GET,
BLOCK_VAR_SET,
BLOCK_VAR_CREATE,
// 函数
BLOCK_FUNC_CALL,
BLOCK_FUNC_RETURN,
BLOCK_TYPE_COUNT
} BlockType;
// 积木类型信息
typedef struct {
BlockType type;
const char* name;
const char* label;
const char* category;
BlockColor color;
int input_count;
int output_count;
SlotValueType input_types[MAX_BLOCK_INPUTS];
SlotValueType output_types[MAX_BLOCK_OUTPUTS];
const char* input_labels[MAX_BLOCK_INPUTS];
const char* output_labels[MAX_BLOCK_OUTPUTS];
} BlockTypeInfo;
// 积木
typedef struct Block {
int id;
BlockType type;
char label[MAX_BLOCK_LABEL];
float x, y; // 画布位置
float width, height; // 尺寸
Color color; // 颜色
BlockSlot inputs[MAX_BLOCK_INPUTS];
int input_count;
BlockSlot outputs[MAX_BLOCK_OUTPUTS];
int output_count;
struct Block* next; // 下一个积木 (堆叠)
struct Block* parent_if; // 所属 if 块 (用于子语句)
struct BlockWorkspace* workspace;
void* user_data;
} Block;
// 连接
typedef struct BlockConnection {
BlockSlot* from;
BlockSlot* to;
float points[8]; // 贝塞尔曲线控制点
} BlockConnection;
// 函数声明
Block* block_create(BlockType type, float x, float y);
Block* block_copy(const Block* src);
void block_destroy(Block* block);
BlockSlot* block_add_input(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
BlockSlot* block_add_output(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
bool block_connect(BlockSlot* from, BlockSlot* to);
void block_disconnect(BlockSlot* slot);
SlotValue block_slot_get_value(BlockSlot* slot);
void block_slot_set_value(BlockSlot* slot, SlotValue value);
bool block_contains_point(Block* block, float px, float py);
void block_slot_world_pos(BlockSlot* slot, float* wx, float* wy);
+225
View File
@@ -0,0 +1,225 @@
// ============================================================================
// block_editor.c - 编辑器主界面实现
// ============================================================================
#include "block_editor.h"
#include "block_types.h"
#include <stdlib.h>
#include <string.h>
int block_editor_init(BlockEditor* editor, void* device, void* context, int width, int height) {
memset(editor, 0, sizeof(BlockEditor));
editor->width = width;
editor->height = height;
editor->toolbar_height = 30;
editor->properties_width = 200;
editor->visible = 0;
// 创建工作区
editor->workspace = workspace_create();
if (!editor->workspace) return 0;
// 初始化渲染器
if (!block_renderer_init(&editor->renderer, device, context)) {
workspace_destroy(editor->workspace);
return 0;
}
// 初始化交互
interaction_init(&editor->interaction);
// 初始化面板
palette_init(&editor->palette);
editor->palette.x = 0;
editor->palette.y = editor->toolbar_height;
// 创建执行器
editor->executor = executor_create(editor->workspace);
return 1;
}
void block_editor_shutdown(BlockEditor* editor) {
if (editor->executor) executor_destroy(editor->executor);
block_renderer_shutdown(&editor->renderer);
if (editor->workspace) workspace_destroy(editor->workspace);
}
void block_editor_show(BlockEditor* editor) {
editor->visible = 1;
}
void block_editor_hide(BlockEditor* editor) {
editor->visible = 0;
}
int block_editor_is_visible(BlockEditor* editor) {
return editor->visible;
}
void block_editor_update(BlockEditor* editor, double delta_time) {
if (!editor->visible) return;
// 更新交互
interaction_update(&editor->interaction, editor->workspace);
// 更新执行器
if (editor->executor->state == EXEC_RUNNING) {
executor_update(editor->executor, delta_time);
}
}
void block_editor_render(BlockEditor* editor) {
if (!editor->visible) return;
block_renderer_begin(&editor->renderer);
// 绘制工具栏
block_renderer_add_quad(&editor->renderer, 0, 0, (float)editor->width, (float)editor->toolbar_height,
0.2f, 0.2f, 0.25f, 1.0f);
block_renderer_draw_text(&editor->renderer, "积木编辑器", 5, 5, 1.0f, 1.0f, 1.0f);
// 绘制面板
palette_render(&editor->palette, &editor->renderer);
// 绘制工作区积木
float ws_x = editor->palette.width;
float ws_y = editor->toolbar_height;
float ws_w = (float)editor->width - editor->palette.width - editor->properties_width;
float ws_h = (float)editor->height - editor->toolbar_height;
// 工作区背景
block_renderer_add_quad(&editor->renderer, ws_x, ws_y, ws_w, ws_h,
0.12f, 0.12f, 0.15f, 1.0f);
// 绘制连接线
for (int i = 0; i < editor->workspace->connection_count; i++) {
BlockConnection* conn = &editor->workspace->connections[i];
if (conn->from && conn->to) {
block_renderer_draw_connection(&editor->renderer, conn->from, conn->to,
editor->workspace->view_x, editor->workspace->view_y, editor->workspace->zoom);
}
}
// 绘制积木
for (int i = 0; i < editor->workspace->block_count; i++) {
Block* block = editor->workspace->blocks[i];
block_renderer_draw_block(&editor->renderer, block,
editor->workspace->view_x, editor->workspace->view_y, editor->workspace->zoom);
// 高亮选中的积木
if (block == editor->workspace->selected_block) {
float x = (block->x - editor->workspace->view_x) * editor->workspace->zoom;
float y = (block->y - editor->workspace->view_y) * editor->workspace->zoom;
float w = block->width * editor->workspace->zoom;
float h = block->height * editor->workspace->zoom;
block_renderer_add_quad(&editor->renderer, x - 2, y - 2, w + 4, h + 4,
1.0f, 1.0f, 0.0f, 0.3f);
}
}
// 绘制正在拖拽的连接线
if (editor->interaction.connecting_from) {
float x1, y1;
block_slot_world_pos(editor->interaction.connecting_from, &x1, &y1);
x1 = (x1 - editor->workspace->view_x) * editor->workspace->zoom;
y1 = (y1 - editor->workspace->view_y) * editor->workspace->zoom;
float x2 = (float)editor->interaction.mouse_x;
float y2 = (float)editor->interaction.mouse_y;
block_renderer_add_line(&editor->renderer, x1, y1, x2, y2, 1.0f, 1.0f, 0.0f, 0.8f);
}
block_renderer_end(&editor->renderer);
}
void block_editor_on_mouse_move(BlockEditor* editor, int x, int y) {
if (!editor->visible) return;
interaction_mouse_move(&editor->interaction, editor->workspace, x, y);
}
void block_editor_on_mouse_down(BlockEditor* editor, int button) {
if (!editor->visible) return;
int x = editor->interaction.mouse_x;
int y = editor->interaction.mouse_y;
// 检查面板点击
if (x < editor->palette.width && y >= editor->toolbar_height) {
BlockType type = palette_handle_click(&editor->palette, (float)x, (float)y);
if (type >= 0) {
block_editor_create_block_from_palette(editor, type, 300, 100);
}
return;
}
interaction_mouse_down(&editor->interaction, editor->workspace, button);
}
void block_editor_on_mouse_up(BlockEditor* editor, int button) {
if (!editor->visible) return;
interaction_mouse_up(&editor->interaction, editor->workspace, button);
}
void block_editor_on_mouse_wheel(BlockEditor* editor, int delta) {
if (!editor->visible) return;
interaction_mouse_wheel(&editor->interaction, editor->workspace, delta);
}
void block_editor_on_key(BlockEditor* editor, int key) {
if (!editor->visible) return;
interaction_key_down(&editor->interaction, editor->workspace, key);
}
void block_editor_on_resize(BlockEditor* editor, int width, int height) {
editor->width = width;
editor->height = height;
}
void block_editor_create_block_from_palette(BlockEditor* editor, BlockType type, float x, float y) {
Block* block = block_create(type, x, y);
if (!block) return;
const BlockTypeInfo* info = block_type_get_info(type);
if (info) {
strncpy(block->label, info->label, MAX_BLOCK_LABEL - 1);
// 创建输入槽位
for (int i = 0; i < info->input_count; i++) {
block_add_input(block,
i == 0 ? SLOT_STATEMENT : SLOT_VALUE,
info->input_types[i],
info->input_labels[i]);
}
// 创建输出槽位
for (int i = 0; i < info->output_count; i++) {
block_add_output(block,
info->output_types[i] == SLOT_VALUE_ANY ? SLOT_STATEMENT : SLOT_VALUE,
info->output_types[i],
info->output_labels[i]);
}
}
workspace_add_block(editor->workspace, block);
}
int block_editor_save(BlockEditor* editor, const char* filepath) {
return serializer_save(editor->workspace, filepath);
}
int block_editor_load(BlockEditor* editor, const char* filepath) {
return serializer_load(editor->workspace, filepath);
}
void block_editor_run(BlockEditor* editor) {
executor_start(editor->executor);
}
void block_editor_stop(BlockEditor* editor) {
executor_stop(editor->executor);
}
void block_editor_pause(BlockEditor* editor) {
executor_pause(editor->executor);
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
// ============================================================================
// block_editor.h - 编辑器主界面
// ============================================================================
#include "block_workspace.h"
#include "block_renderer.h"
#include "block_interaction.h"
#include "block_palette.h"
#include "block_executor.h"
#include "block_serializer.h"
typedef struct BlockEditor {
BlockWorkspace* workspace;
BlockRenderer renderer;
BlockInteraction interaction;
BlockPalette palette;
BlockExecutor* executor;
int width, height;
int visible;
// 工具栏
int toolbar_height;
// 属性面板
int properties_width;
Block* selected_block;
} BlockEditor;
// 初始化/销毁
int block_editor_init(BlockEditor* editor, void* device, void* context, int width, int height);
void block_editor_shutdown(BlockEditor* editor);
// 显示/隐藏
void block_editor_show(BlockEditor* editor);
void block_editor_hide(BlockEditor* editor);
int block_editor_is_visible(BlockEditor* editor);
// 更新/渲染
void block_editor_update(BlockEditor* editor, double delta_time);
void block_editor_render(BlockEditor* editor);
// 输入处理
void block_editor_on_mouse_move(BlockEditor* editor, int x, int y);
void block_editor_on_mouse_down(BlockEditor* editor, int button);
void block_editor_on_mouse_up(BlockEditor* editor, int button);
void block_editor_on_mouse_wheel(BlockEditor* editor, int delta);
void block_editor_on_key(BlockEditor* editor, int key);
void block_editor_on_resize(BlockEditor* editor, int width, int height);
// 从面板拖拽创建积木
void block_editor_create_block_from_palette(BlockEditor* editor, BlockType type, float x, float y);
// 保存/加载
int block_editor_save(BlockEditor* editor, const char* filepath);
int block_editor_load(BlockEditor* editor, const char* filepath);
// 运行控制
void block_editor_run(BlockEditor* editor);
void block_editor_stop(BlockEditor* editor);
void block_editor_pause(BlockEditor* editor);
+465
View File
@@ -0,0 +1,465 @@
// ============================================================================
// block_executor.c - 积木执行引擎 (完整实现)
// ============================================================================
#include "block_executor.h"
#include "block_types.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#include <stdarg.h>
// 日志回调
typedef void (*LogCallback)(const char* msg, int level);
static LogCallback g_log_cb = NULL;
static void log_info(const char* fmt, ...) {
char buf[512]; va_list args; va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args); va_end(args);
if (g_log_cb) g_log_cb(buf, 0); else printf("[INFO] %s\n", buf);
}
static void log_error(const char* fmt, ...) {
char buf[512]; va_list args; va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args); va_end(args);
if (g_log_cb) g_log_cb(buf, 2); else printf("[ERROR] %s\n", buf);
}
void executor_set_log_callback(LogCallback cb) { g_log_cb = cb; }
// 值辅助
static SlotValue make_number(double v) { SlotValue v2; v2.type = SLOT_VALUE_NUMBER; v2.as.number = v; return v2; }
static SlotValue make_string(const char* s) { SlotValue v; v.type = SLOT_VALUE_STRING; strncpy(v.as.string, s, 255); return v; }
static SlotValue make_bool(int b) { SlotValue v; v.type = SLOT_VALUE_BOOLEAN; v.as.boolean = b; return v; }
static SlotValue make_vec3(float x, float y, float z) { SlotValue v; v.type = SLOT_VALUE_VECTOR3; v.as.vector3.x = x; v.as.vector3.y = y; v.as.vector3.z = z; return v; }
// 连接追踪:获取槽位的输入值(通过连线回溯)
static SlotValue get_connected_value(BlockSlot* slot) {
if (slot->connected) return slot->connected->value;
return slot->value;
}
BlockExecutor* executor_create(BlockWorkspace* ws) {
BlockExecutor* exec = (BlockExecutor*)calloc(1, sizeof(BlockExecutor));
if (!exec) return NULL;
exec->workspace = ws;
return exec;
}
void executor_destroy(BlockExecutor* exec) { free(exec); }
void executor_start(BlockExecutor* exec) {
exec->state = EXEC_RUNNING;
exec->call_stack_top = 0;
exec->loop_stack_top = 0;
exec->current_block = NULL;
for (int i = 0; i < exec->workspace->block_count; i++) {
Block* block = exec->workspace->blocks[i];
if (block->type == BLOCK_EVENT_START) {
exec->current_block = block;
log_info("执行器启动: 找到 on_start 事件");
return;
}
}
log_error("未找到 on_start 事件积木");
exec->state = EXEC_IDLE;
}
void executor_stop(BlockExecutor* exec) { exec->state = EXEC_IDLE; exec->current_block = NULL; }
void executor_pause(BlockExecutor* exec) { if (exec->state == EXEC_RUNNING) exec->state = EXEC_PAUSED; }
void executor_resume(BlockExecutor* exec) { if (exec->state == EXEC_PAUSED) exec->state = EXEC_RUNNING; }
// 获取下一个执行的积木(通过输出端口连接)
static Block* next_from_output(Block* block, int output_index) {
if (output_index >= block->output_count) return NULL;
BlockSlot* out = &block->outputs[output_index];
if (out->connected && out->connected->owner) return out->connected->owner;
return NULL;
}
// 查找循环体链的最后一个积木(用于确定循环结束后应跳转到的积木)
static Block* find_loop_body_end(Block* loop_block) {
if (!loop_block || loop_block->output_count == 0 || !loop_block->outputs[0].connected) return NULL;
Block* cur = loop_block->outputs[0].connected->owner;
Block* prev = loop_block;
int guard = 0;
while (cur && guard < 1000) {
guard++;
if (cur == loop_block) return prev; // 回到循环头,则前一个为体尾
Block* next = NULL;
if (cur->output_count > 0 && cur->outputs[0].connected) {
next = cur->outputs[0].connected->owner;
}
if (!next) return cur;
prev = cur;
cur = next;
}
return cur;
}
// 获取值输入
static SlotValue get_input(Block* block, int idx) {
if (idx >= block->input_count) return make_number(0);
return get_connected_value(&block->inputs[idx]);
}
void executor_step(BlockExecutor* exec) {
if (!exec->current_block) { exec->state = EXEC_IDLE; return; }
Block* cur = exec->current_block;
Block* next = NULL;
switch (cur->type) {
// === 事件 ===
case BLOCK_EVENT_START:
case BLOCK_EVENT_UPDATE:
case BLOCK_EVENT_COLLISION:
case BLOCK_EVENT_MOUSE_CLICK:
next = next_from_output(cur, 0);
break;
case BLOCK_EVENT_KEY_PRESSED: {
const char* key = get_input(cur, 0).as.string;
log_info("事件: 按键 '%s' (需要引擎集成)", key);
next = next_from_output(cur, 0);
break;
}
case BLOCK_EVENT_TIMER: {
double interval = get_input(cur, 0).as.number;
log_info("事件: 定时器 %.1fs (需要引擎集成)", interval);
next = next_from_output(cur, 0);
break;
}
// === 动作 ===
case BLOCK_ACTION_MOVE: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("移动物体: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_ROTATE: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("旋转物体: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_SCALE: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("缩放物体: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_SET_POSITION: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("设置位置: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_SET_ROTATION: {
double x = get_input(cur, 1).as.number;
double y = get_input(cur, 2).as.number;
double z = get_input(cur, 3).as.number;
log_info("设置旋转: (%.2f, %.2f, %.2f)", x, y, z);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_PLAY_SOUND: {
const char* name = get_input(cur, 1).as.string;
log_info("播放音效: '%s'", name);
next = next_from_output(cur, 0);
break;
}
case BLOCK_ACTION_DESTROY:
log_info("销毁物体");
next = next_from_output(cur, 0);
break;
case BLOCK_ACTION_INSTANTIATE: {
const char* prefab = get_input(cur, 1).as.string;
log_info("实例化预制体: '%s'", prefab);
next = next_from_output(cur, 0);
break;
}
// === 控制流 ===
case BLOCK_CONTROL_IF:
case BLOCK_CONTROL_IF_ELSE: {
int cond = get_input(cur, 1).as.boolean;
log_info("如果 %s", cond ? "真 -> Then" : "假 -> Else");
next = next_from_output(cur, cond ? 0 : 1);
break;
}
case BLOCK_CONTROL_REPEAT:
case BLOCK_CONTROL_WHILE: {
int cond = get_input(cur, 1).as.boolean;
LoopFrame* frame = NULL;
if (exec->loop_stack_top > 0 && exec->loop_stack[exec->loop_stack_top - 1].loop_block == cur) {
frame = &exec->loop_stack[exec->loop_stack_top - 1];
} else {
if (exec->loop_stack_top < MAX_LOOP_STACK) {
frame = &exec->loop_stack[exec->loop_stack_top++];
frame->loop_block = cur;
frame->counter = 0;
frame->target = 0;
Block* body_end = find_loop_body_end(cur);
frame->after_block = body_end ? next_from_output(body_end, 0) : NULL;
}
}
if (frame && cond) {
frame->counter++;
next = next_from_output(cur, 0);
} else {
if (frame) exec->loop_stack_top--;
next = frame ? frame->after_block : next_from_output(cur, 0);
}
break;
}
case BLOCK_CONTROL_REPEAT_TIMES: {
double times = get_input(cur, 1).as.number;
LoopFrame* frame = NULL;
if (exec->loop_stack_top > 0 && exec->loop_stack[exec->loop_stack_top - 1].loop_block == cur) {
frame = &exec->loop_stack[exec->loop_stack_top - 1];
frame->counter++;
} else {
if (exec->loop_stack_top < MAX_LOOP_STACK) {
frame = &exec->loop_stack[exec->loop_stack_top++];
frame->loop_block = cur;
frame->counter = 0;
frame->target = (int)times;
Block* body_end = find_loop_body_end(cur);
frame->after_block = body_end ? next_from_output(body_end, 0) : NULL;
}
}
if (frame && frame->counter < frame->target) {
log_info("循环 %d / %d", frame->counter + 1, frame->target);
next = next_from_output(cur, 0);
} else {
if (frame) exec->loop_stack_top--;
next = frame ? frame->after_block : next_from_output(cur, 0);
}
break;
}
case BLOCK_CONTROL_WAIT: {
double secs = get_input(cur, 1).as.number;
log_info("等待 %.1f 秒", secs);
exec->wait_timer = secs;
exec->state = EXEC_WAITING;
exec->current_block = next_from_output(cur, 0);
return;
}
case BLOCK_CONTROL_FUNCTION_DEF:
log_info("函数定义 (跳过)");
next = next_from_output(cur, 0);
break;
// === 运算 ===
case BLOCK_OP_ADD: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(a + b);
log_info("%.1f + %.1f = %.1f", a, b, a + b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_SUB: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(a - b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_MUL: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(a * b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_DIV: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(b != 0 ? a / b : 0);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_MOD: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_number(b != 0 ? fmod(a, b) : 0);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_EQUALS: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a == b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_NOT_EQUALS: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a != b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_GREATER: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a > b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_LESS: {
double a = get_input(cur, 0).as.number;
double b = get_input(cur, 1).as.number;
cur->outputs[0].value = make_bool(a < b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_AND: {
int a = get_input(cur, 0).as.boolean;
int b = get_input(cur, 1).as.boolean;
cur->outputs[0].value = make_bool(a && b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_OR: {
int a = get_input(cur, 0).as.boolean;
int b = get_input(cur, 1).as.boolean;
cur->outputs[0].value = make_bool(a || b);
next = next_from_output(cur, 0);
break;
}
case BLOCK_OP_NOT: {
int a = get_input(cur, 0).as.boolean;
cur->outputs[0].value = make_bool(!a);
next = next_from_output(cur, 0);
break;
}
// === 变量 ===
case BLOCK_VAR_GET: {
const char* name = cur->label;
SlotValue val = executor_get_variable(exec, name);
cur->outputs[0].value = val;
log_info("读取变量 '%s' = %.2f", name, val.as.number);
next = next_from_output(cur, 0);
break;
}
case BLOCK_VAR_SET: {
const char* name = cur->label;
SlotValue val = get_input(cur, 1);
executor_set_variable(exec, name, val);
log_info("设置变量 '%s' = %.2f", name, val.as.number);
next = next_from_output(cur, 0);
break;
}
case BLOCK_VAR_CREATE: {
const char* name = cur->label;
executor_create_variable(exec, name);
log_info("创建变量 '%s' = 0", name);
next = next_from_output(cur, 0);
break;
}
// === 函数 ===
case BLOCK_FUNC_CALL: {
const char* fname = get_input(cur, 1).as.string;
log_info("调用函数 '%s'", fname);
// 在工作区中查找同名函数定义积木
Block* def = NULL;
for (int i = 0; i < exec->workspace->block_count; i++) {
Block* b = exec->workspace->blocks[i];
if (b->type == BLOCK_CONTROL_FUNCTION_DEF && strcmp(b->label, fname) == 0) {
def = b;
break;
}
}
if (def && exec->call_stack_top < MAX_CALL_STACK) {
exec->call_stack[exec->call_stack_top].block = cur;
exec->call_stack[exec->call_stack_top].return_block = next_from_output(cur, 0);
exec->call_stack_top++;
next = next_from_output(def, 0);
} else {
if (!def) log_error("未找到函数定义: '%s'", fname);
next = next_from_output(cur, 0);
}
break;
}
case BLOCK_FUNC_RETURN: {
if (exec->call_stack_top > 0) {
exec->call_stack_top--;
next = exec->call_stack[exec->call_stack_top].return_block;
}
break;
}
default:
log_error("未知积木类型: %d", cur->type);
next = next_from_output(cur, 0);
break;
}
// 若当前 next 是某个活跃循环的 after_block,则跳转回循环头继续判断
if (next && exec->loop_stack_top > 0) {
LoopFrame* frame = &exec->loop_stack[exec->loop_stack_top - 1];
if (next == frame->after_block) {
next = frame->loop_block;
}
}
exec->current_block = next;
if (!exec->current_block) {
log_info("执行完成");
exec->state = EXEC_IDLE;
}
}
void executor_update(BlockExecutor* exec, double delta_time) {
if (exec->state == EXEC_WAITING) {
exec->wait_timer -= delta_time;
if (exec->wait_timer <= 0) exec->state = EXEC_RUNNING;
return;
}
if (exec->state != EXEC_RUNNING) return;
int steps = 0;
while (exec->current_block && exec->state == EXEC_RUNNING && steps < 1000) {
executor_step(exec);
steps++;
}
}
void executor_set_variable(BlockExecutor* exec, const char* name, SlotValue value) {
for (int i = 0; i < exec->variable_count; i++) {
if (strcmp(exec->variables[i].name, name) == 0) {
exec->variables[i].value = value;
return;
}
}
if (exec->variable_count < MAX_VARIABLES) {
Variable* var = &exec->variables[exec->variable_count++];
strncpy(var->name, name, 63);
var->value = value;
var->used = 1;
}
}
SlotValue executor_get_variable(BlockExecutor* exec, const char* name) {
for (int i = 0; i < exec->variable_count; i++) {
if (strcmp(exec->variables[i].name, name) == 0) return exec->variables[i].value;
}
return make_number(0);
}
void executor_create_variable(BlockExecutor* exec, const char* name) {
executor_set_variable(exec, name, make_number(0));
}
@@ -0,0 +1,86 @@
#pragma once
// ============================================================================
// block_executor.h - 积木执行引擎
// ============================================================================
#include "block_workspace.h"
#define MAX_VARIABLES 256
#define MAX_CALL_STACK 32
#define MAX_EXEC_STACK 1024
#define MAX_LOOP_STACK 32
// 变量
typedef struct {
char name[64];
SlotValue value;
int used;
} Variable;
// 调用帧
typedef struct {
Block* block;
Block* return_block;
} CallFrame;
// 循环帧
typedef struct {
Block* loop_block;
int counter;
int target;
Block* after_block;
} LoopFrame;
// 执行状态
typedef enum {
EXEC_IDLE = 0,
EXEC_RUNNING,
EXEC_WAITING,
EXEC_PAUSED,
EXEC_ERROR
} ExecState;
// 执行器
typedef struct BlockExecutor {
BlockWorkspace* workspace;
ExecState state;
Variable variables[MAX_VARIABLES];
int variable_count;
CallFrame call_stack[MAX_CALL_STACK];
int call_stack_top;
LoopFrame loop_stack[MAX_LOOP_STACK];
int loop_stack_top;
Block* current_block;
double wait_timer;
int breakpoint_enabled;
Block* breakpoint_block;
} BlockExecutor;
// 初始化/销毁
BlockExecutor* executor_create(BlockWorkspace* ws);
void executor_destroy(BlockExecutor* exec);
// 控制
void executor_start(BlockExecutor* exec);
void executor_stop(BlockExecutor* exec);
void executor_pause(BlockExecutor* exec);
void executor_resume(BlockExecutor* exec);
void executor_step(BlockExecutor* exec);
// 更新 (每帧调用)
void executor_update(BlockExecutor* exec, double delta_time);
// 变量操作
void executor_set_variable(BlockExecutor* exec, const char* name, SlotValue value);
SlotValue executor_get_variable(BlockExecutor* exec, const char* name);
void executor_create_variable(BlockExecutor* exec, const char* name);
// 日志回调
typedef void (*LogCallback)(const char* msg, int level);
void executor_set_log_callback(LogCallback cb);
+99
View File
@@ -0,0 +1,99 @@
// 5x7 位图字体 (列优先, LSB 在上)
static const uint8_t BLOCK_FONT[96][5] = {
/* */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* ! */ {0x00, 0x00, 0xBE, 0x00, 0x00},
/* " */ {0x00, 0x36, 0x36, 0x00, 0x00},
/* # */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* $ */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* % */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* & */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* ' */ {0x00, 0x06, 0x06, 0x00, 0x00},
/* ( */ {0x3C, 0x42, 0x81, 0x81, 0x00},
/* ) */ {0x00, 0x81, 0x81, 0x42, 0x3C},
/* * */ {0x22, 0x14, 0x08, 0x14, 0x22},
/* + */ {0x08, 0x08, 0x7F, 0x08, 0x08},
/* , */ {0x00, 0xA0, 0x60, 0x00, 0x00},
/* - */ {0x08, 0x08, 0x08, 0x08, 0x08},
/* . */ {0x00, 0x60, 0x60, 0x00, 0x00},
/* / */ {0x80, 0x40, 0x20, 0x10, 0x08},
/* 0 */ {0x7E, 0x81, 0x81, 0x81, 0x7E},
/* 1 */ {0x00, 0x82, 0xFF, 0x80, 0x00},
/* 2 */ {0xC2, 0xA1, 0x91, 0x89, 0x86},
/* 3 */ {0x42, 0x81, 0x89, 0x89, 0x76},
/* 4 */ {0x18, 0x28, 0x48, 0xFF, 0x08},
/* 5 */ {0x4F, 0x89, 0x89, 0x89, 0x71},
/* 6 */ {0x7E, 0x89, 0x89, 0x89, 0x72},
/* 7 */ {0x01, 0xC1, 0x31, 0x0D, 0x03},
/* 8 */ {0x76, 0x89, 0x89, 0x89, 0x76},
/* 9 */ {0x46, 0x89, 0x89, 0x89, 0x7E},
/* : */ {0x00, 0x66, 0x66, 0x00, 0x00},
/* ; */ {0x00, 0xB6, 0x66, 0x00, 0x00},
/* < */ {0x10, 0x28, 0x44, 0x82, 0x00},
/* = */ {0x28, 0x28, 0x28, 0x28, 0x28},
/* > */ {0x00, 0x82, 0x44, 0x28, 0x10},
/* ? */ {0x02, 0x01, 0x51, 0x09, 0x06},
/* @ */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* A */ {0x7E, 0x09, 0x09, 0x09, 0x7E},
/* B */ {0xFF, 0x89, 0x89, 0x89, 0x76},
/* C */ {0x7E, 0x81, 0x81, 0x81, 0x42},
/* D */ {0xFF, 0x81, 0x81, 0x42, 0x3C},
/* E */ {0xFF, 0x89, 0x89, 0x89, 0x81},
/* F */ {0xFF, 0x09, 0x09, 0x09, 0x01},
/* G */ {0x7E, 0x81, 0x89, 0x89, 0x72},
/* H */ {0xFF, 0x08, 0x08, 0x08, 0xFF},
/* I */ {0x00, 0x81, 0xFF, 0x81, 0x00},
/* J */ {0x40, 0x80, 0x80, 0x80, 0x7F},
/* K */ {0xFF, 0x08, 0x14, 0x22, 0x41},
/* L */ {0xFF, 0x80, 0x80, 0x80, 0x80},
/* M */ {0xFF, 0x02, 0x0C, 0x02, 0xFF},
/* N */ {0xFF, 0x04, 0x08, 0x10, 0xFF},
/* O */ {0x7E, 0x81, 0x81, 0x81, 0x7E},
/* P */ {0xFF, 0x09, 0x09, 0x09, 0x06},
/* Q */ {0x7E, 0x81, 0xA1, 0x41, 0xBE},
/* R */ {0xFF, 0x09, 0x19, 0x29, 0x46},
/* S */ {0x46, 0x89, 0x89, 0x89, 0x72},
/* T */ {0x01, 0x01, 0xFF, 0x01, 0x01},
/* U */ {0x7F, 0x80, 0x80, 0x80, 0x7F},
/* V */ {0x3F, 0x40, 0x80, 0x40, 0x3F},
/* W */ {0x7F, 0x80, 0x70, 0x80, 0x7F},
/* X */ {0xC3, 0x24, 0x18, 0x24, 0xC3},
/* Y */ {0x03, 0x04, 0xF8, 0x04, 0x03},
/* Z */ {0xC1, 0xA1, 0x91, 0x89, 0x87},
/* [ */ {0x7E, 0x82, 0x82, 0x82, 0x00},
/* \ */ {0x08, 0x10, 0x20, 0x40, 0x80},
/* ] */ {0x00, 0x82, 0x82, 0x82, 0x7E},
/* ^ */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* _ */ {0x80, 0x80, 0x80, 0x80, 0x80},
/* ` */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* a */ {0x20, 0x54, 0x54, 0x54, 0x78},
/* b */ {0xFF, 0x48, 0x48, 0x48, 0x30},
/* c */ {0x38, 0x44, 0x44, 0x44, 0x44},
/* d */ {0x30, 0x48, 0x48, 0x48, 0xFF},
/* e */ {0x38, 0x54, 0x54, 0x54, 0x58},
/* f */ {0x08, 0x7E, 0x09, 0x01, 0x02},
/* g */ {0x08, 0x54, 0x54, 0x54, 0x3C},
/* h */ {0xFF, 0x08, 0x08, 0x08, 0xF0},
/* i */ {0x00, 0x48, 0x7A, 0x40, 0x00},
/* j */ {0x20, 0x40, 0x40, 0x3A, 0x00},
/* k */ {0xFF, 0x10, 0x28, 0x44, 0x82},
/* l */ {0x00, 0x41, 0xFF, 0x40, 0x00},
/* m */ {0x7C, 0x04, 0x78, 0x04, 0x78},
/* n */ {0x7C, 0x04, 0x04, 0x04, 0xF8},
/* o */ {0x38, 0x44, 0x44, 0x44, 0x38},
/* p */ {0xFC, 0x24, 0x24, 0x24, 0x18},
/* q */ {0x18, 0x24, 0x24, 0x24, 0xFC},
/* r */ {0x7C, 0x08, 0x04, 0x04, 0x08},
/* s */ {0x48, 0x54, 0x54, 0x54, 0x24},
/* t */ {0x04, 0x3F, 0x44, 0x40, 0x20},
/* u */ {0x3C, 0x40, 0x40, 0x40, 0x3C},
/* v */ {0x1C, 0x20, 0x40, 0x20, 0x1C},
/* w */ {0x3C, 0x40, 0x30, 0x40, 0x3C},
/* x */ {0x44, 0x28, 0x10, 0x28, 0x44},
/* y */ {0x0C, 0x50, 0x50, 0x50, 0x3C},
/* z */ {0x44, 0x64, 0x54, 0x4C, 0x44},
/* { */ {0x18, 0x24, 0x42, 0x24, 0x18},
/* | */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* } */ {0x18, 0x24, 0x42, 0x24, 0x18},
/* ~ */ {0x00, 0x00, 0x00, 0x00, 0x00},
/* \x7f */ {0x00, 0x00, 0x00, 0x00, 0x00},
};
@@ -0,0 +1,151 @@
// ============================================================================
// block_interaction.c - 交互系统实现
// ============================================================================
#include "block_interaction.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <Windows.h>
void interaction_init(BlockInteraction* inter) {
memset(inter, 0, sizeof(BlockInteraction));
}
void interaction_mouse_move(BlockInteraction* inter, BlockWorkspace* ws, int x, int y) {
inter->mouse_x = x;
inter->mouse_y = y;
// 拖拽积木
if (inter->dragging_block) {
float wx, wy;
workspace_screen_to_world(ws, (float)x, (float)y, &wx, &wy);
inter->dragging_block->x = wx - inter->drag_offset_x;
inter->dragging_block->y = wy - inter->drag_offset_y;
}
// 平移画布
if (inter->panning) {
float dx = (x - inter->pan_start_x) / ws->zoom;
float dy = (y - inter->pan_start_y) / ws->zoom;
ws->view_x = inter->view_start_x - dx;
ws->view_y = inter->view_start_y - dy;
}
}
void interaction_mouse_down(BlockInteraction* inter, BlockWorkspace* ws, int button) {
inter->mouse_down = 1;
inter->mouse_button = button;
float wx, wy;
workspace_screen_to_world(ws, (float)inter->mouse_x, (float)inter->mouse_y, &wx, &wy);
if (button == 0) { // 左键
// 检查是否点击槽位
BlockSlot* slot = workspace_find_slot_at(ws, wx, wy);
if (slot) {
// 开始连接
inter->connecting_from = slot;
inter->connect_start_x = wx;
inter->connect_start_y = wy;
return;
}
// 检查是否点击积木
Block* block = workspace_find_block_at(ws, wx, wy);
if (block) {
inter->dragging_block = block;
inter->drag_offset_x = wx - block->x;
inter->drag_offset_y = wy - block->y;
inter->selected_block = block;
ws->selected_block = block;
return;
}
// 点击空白区域: 取消选择
inter->selected_block = NULL;
ws->selected_block = NULL;
}
if (button == 1 || button == 2) { // 右键/中键: 平移
inter->panning = 1;
inter->pan_start_x = (float)inter->mouse_x;
inter->pan_start_y = (float)inter->mouse_y;
inter->view_start_x = ws->view_x;
inter->view_start_y = ws->view_y;
}
}
void interaction_mouse_up(BlockInteraction* inter, BlockWorkspace* ws, int button) {
inter->mouse_down = 0;
if (button == 0) {
// 结束拖拽积木
inter->dragging_block = NULL;
// 结束连接
if (inter->connecting_from) {
float wx, wy;
workspace_screen_to_world(ws, (float)inter->mouse_x, (float)inter->mouse_y, &wx, &wy);
BlockSlot* target = workspace_find_slot_at(ws, wx, wy);
if (target && target != inter->connecting_from) {
// 尝试建立连接
workspace_add_connection(ws, inter->connecting_from, target);
}
inter->connecting_from = NULL;
}
}
if (button == 1 || button == 2) {
inter->panning = 0;
}
}
void interaction_key_down(BlockInteraction* inter, BlockWorkspace* ws, int key) {
inter->key = key;
inter->key_pressed = 1;
// 删除选中的积木
if (key == 46 || key == 8) { // DELETE 或 BACKSPACE
if (inter->selected_block) {
workspace_remove_block(ws, inter->selected_block->id);
inter->selected_block = NULL;
ws->selected_block = NULL;
}
}
// 复制/粘贴 (Ctrl+C / Ctrl+V)
static Block* clipboard = NULL;
if (key == 'C' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
if (inter->selected_block) {
if (clipboard) block_destroy(clipboard);
clipboard = block_copy(inter->selected_block);
}
}
if (key == 'V' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
if (clipboard) {
Block* copy = block_copy(clipboard);
if (copy) {
copy->x += 20.0f;
copy->y += 20.0f;
copy->id = ws->next_block_id++;
workspace_add_block(ws, copy);
inter->selected_block = copy;
ws->selected_block = copy;
}
}
}
}
void interaction_mouse_wheel(BlockInteraction* inter, BlockWorkspace* ws, int delta) {
float zoom_factor = delta > 0 ? 1.1f : 0.9f;
ws->zoom *= zoom_factor;
if (ws->zoom < 0.25f) ws->zoom = 0.25f;
if (ws->zoom > 4.0f) ws->zoom = 4.0f;
}
void interaction_update(BlockInteraction* inter, BlockWorkspace* ws) {
inter->key_pressed = 0;
(void)ws;
}
@@ -0,0 +1,44 @@
#pragma once
// ============================================================================
// block_interaction.h - 交互系统
// ============================================================================
#include "block_workspace.h"
typedef struct {
int mouse_x, mouse_y;
int mouse_down;
int mouse_button; // 0=left, 1=right, 2=middle
int key;
int key_pressed;
// 拖拽状态
Block* dragging_block;
float drag_offset_x, drag_offset_y;
// 连接状态
BlockSlot* connecting_from;
float connect_start_x, connect_start_y;
// 选择状态
Block* selected_block;
// 平移状态
int panning;
float pan_start_x, pan_start_y;
float view_start_x, view_start_y;
} BlockInteraction;
// 初始化
void interaction_init(BlockInteraction* inter);
// 处理输入事件
void interaction_mouse_move(BlockInteraction* inter, BlockWorkspace* ws, int x, int y);
void interaction_mouse_down(BlockInteraction* inter, BlockWorkspace* ws, int button);
void interaction_mouse_up(BlockInteraction* inter, BlockWorkspace* ws, int button);
void interaction_key_down(BlockInteraction* inter, BlockWorkspace* ws, int key);
void interaction_mouse_wheel(BlockInteraction* inter, BlockWorkspace* ws, int delta);
// 更新 (每帧调用)
void interaction_update(BlockInteraction* inter, BlockWorkspace* ws);
+140
View File
@@ -0,0 +1,140 @@
// ============================================================================
// block_palette.c - 积木面板实现
// ============================================================================
#include "block_palette.h"
#include "block_renderer.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void palette_init(BlockPalette* palette) {
memset(palette, 0, sizeof(BlockPalette));
palette->width = 200;
palette->height = 600;
// 添加所有积木类型
int count;
const BlockTypeInfo* all = block_type_get_all(&count);
for (int i = 0; i < count; i++) {
palette_add_block(palette, all[i].type);
}
}
void palette_add_block(BlockPalette* palette, BlockType type) {
const BlockTypeInfo* info = block_type_get_info(type);
if (!info) return;
// 查找或创建分类
for (int i = 0; i < palette->category_count; i++) {
if (strcmp(palette->categories[i].name, info->category) == 0) {
palette->categories[i].blocks[palette->categories[i].block_count++] = type;
return;
}
}
// 创建新分类
if (palette->category_count < MAX_PALETTE_CATEGORIES) {
PaletteCategory* cat = &palette->categories[palette->category_count++];
strncpy(cat->name, info->category, 63);
cat->blocks[cat->block_count++] = type;
cat->expanded = 1;
}
}
void palette_render(BlockPalette* palette, BlockRenderer* renderer) {
// 绘制背景
block_renderer_add_quad(renderer, palette->x, palette->y, palette->width, palette->height,
0.15f, 0.15f, 0.18f, 1.0f);
// 绘制标题
block_renderer_draw_text(renderer, "积木面板", palette->x + 5, palette->y + 5,
1.0f, 1.0f, 1.0f);
float y = palette->y + 25 - palette->scroll_y;
for (int c = 0; c < palette->category_count; c++) {
PaletteCategory* cat = &palette->categories[c];
// 绘制分类标题
block_renderer_add_quad(renderer, palette->x + 2, y, palette->width - 4, 20,
0.25f, 0.25f, 0.3f, 1.0f);
const char* arrow = cat->expanded ? "[-]" : "[+]";
char title[128];
snprintf(title, 128, "%s %s (%d)", arrow, cat->name, cat->block_count);
block_renderer_draw_text(renderer, title, palette->x + 5, y + 3,
0.9f, 0.9f, 0.9f);
y += 22;
if (!cat->expanded) continue;
// 绘制积木列表
for (int b = 0; b < cat->block_count; b++) {
const BlockTypeInfo* info = block_type_get_info(cat->blocks[b]);
if (!info) continue;
// 搜索过滤
if (palette->search_active && palette->search_text[0]) {
if (strstr(info->name, palette->search_text) == NULL &&
strstr(info->label, palette->search_text) == NULL) {
continue;
}
}
// 绘制积木预览
block_renderer_add_quad(renderer, palette->x + 5, y, palette->width - 10, 18,
info->color.r, info->color.g, info->color.b, info->color.a);
block_renderer_draw_text(renderer, info->label, palette->x + 10, y + 3,
1.0f, 1.0f, 1.0f);
y += 20;
}
y += 5;
}
}
BlockType palette_handle_click(BlockPalette* palette, float mx, float my) {
float y = palette->y + 25 - palette->scroll_y;
for (int c = 0; c < palette->category_count; c++) {
PaletteCategory* cat = &palette->categories[c];
// 检查分类标题点击
if (mx >= palette->x && mx <= palette->x + palette->width &&
my >= y && my <= y + 20) {
cat->expanded = !cat->expanded;
return -1;
}
y += 22;
if (!cat->expanded) continue;
// 检查积木点击
for (int b = 0; b < cat->block_count; b++) {
if (mx >= palette->x + 5 && mx <= palette->x + palette->width - 5 &&
my >= y && my <= y + 18) {
return cat->blocks[b];
}
y += 20;
}
y += 5;
}
return -1;
}
void palette_search(BlockPalette* palette, const char* text) {
if (text) {
strncpy(palette->search_text, text, 127);
palette->search_active = 1;
} else {
palette->search_text[0] = '\0';
palette->search_active = 0;
}
}
@@ -0,0 +1,43 @@
#pragma once
// ============================================================================
// block_palette.h - 积木面板
// ============================================================================
#include "block_types.h"
#include "block_workspace.h"
#define MAX_PALETTE_CATEGORIES 16
typedef struct {
char name[64];
BlockType blocks[64];
int block_count;
int expanded;
} PaletteCategory;
typedef struct BlockPalette {
PaletteCategory categories[MAX_PALETTE_CATEGORIES];
int category_count;
float x, y, width, height;
float scroll_y;
char search_text[128];
int search_active;
} BlockPalette;
// 初始化
void palette_init(BlockPalette* palette);
// 添加积木到面板 (按分类)
void palette_add_block(BlockPalette* palette, BlockType type);
struct BlockRenderer;
void palette_render(BlockPalette* palette, struct BlockRenderer* renderer);
// 处理点击
BlockType palette_handle_click(BlockPalette* palette, float mx, float my);
// 搜索
void palette_search(BlockPalette* palette, const char* text);
+190
View File
@@ -0,0 +1,190 @@
#include "block_renderer.h"
#include "block_core.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
// 5x7 位图字体 (列优先, LSB 在上)
#include "block_font.inc"
int block_renderer_init(BlockRenderer* renderer, void* device, void* context) {
memset(renderer, 0, sizeof(BlockRenderer));
renderer->device = (ID3D11Device*)device;
renderer->context = (ID3D11DeviceContext*)context;
renderer->vertex_capacity = 65536;
renderer->vertex_data = (float*)malloc(renderer->vertex_capacity * 7 * sizeof(float));
renderer->index_capacity = 65536;
renderer->index_data = (uint32_t*)malloc(renderer->index_capacity * sizeof(uint32_t));
return 1;
}
void block_renderer_shutdown(BlockRenderer* renderer) {
if (renderer->vertex_data) free(renderer->vertex_data);
if (renderer->index_data) free(renderer->index_data);
if (renderer->vertex_buffer) renderer->vertex_buffer->lpVtbl->Release(renderer->vertex_buffer);
if (renderer->index_buffer) renderer->index_buffer->lpVtbl->Release(renderer->index_buffer);
if (renderer->vertex_shader) renderer->vertex_shader->lpVtbl->Release(renderer->vertex_shader);
if (renderer->pixel_shader) renderer->pixel_shader->lpVtbl->Release(renderer->pixel_shader);
if (renderer->input_layout) renderer->input_layout->lpVtbl->Release(renderer->input_layout);
memset(renderer, 0, sizeof(BlockRenderer));
}
void block_renderer_begin(BlockRenderer* renderer) {
renderer->vertex_count = 0;
renderer->index_count = 0;
}
void block_renderer_end(BlockRenderer* renderer) {
if (renderer->vertex_count == 0) return;
ID3D11DeviceContext* ctx = renderer->context;
D3D11_MAPPED_SUBRESOURCE mapped;
if (SUCCEEDED(ctx->lpVtbl->Map(ctx, (ID3D11Resource*)renderer->vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
memcpy(mapped.pData, renderer->vertex_data, renderer->vertex_count * 7 * sizeof(float));
ctx->lpVtbl->Unmap(ctx, (ID3D11Resource*)renderer->vertex_buffer, 0);
}
if (renderer->index_count > 0) {
if (SUCCEEDED(ctx->lpVtbl->Map(ctx, (ID3D11Resource*)renderer->index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) {
memcpy(mapped.pData, renderer->index_data, renderer->index_count * sizeof(uint32_t));
ctx->lpVtbl->Unmap(ctx, (ID3D11Resource*)renderer->index_buffer, 0);
}
}
UINT stride = 7 * sizeof(float);
UINT offset = 0;
ID3D11Buffer* vb = renderer->vertex_buffer;
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &vb, &stride, &offset);
if (renderer->index_buffer) {
ctx->lpVtbl->IASetIndexBuffer(ctx, renderer->index_buffer, DXGI_FORMAT_R32_UINT, 0);
}
ctx->lpVtbl->IASetInputLayout(ctx, renderer->input_layout);
ctx->lpVtbl->IASetPrimitiveTopology(ctx, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ctx->lpVtbl->VSSetShader(ctx, renderer->vertex_shader, NULL, 0);
ctx->lpVtbl->PSSetShader(ctx, renderer->pixel_shader, NULL, 0);
if (renderer->index_count > 0) {
ctx->lpVtbl->DrawIndexed(ctx, renderer->index_count, 0, 0);
} else {
ctx->lpVtbl->Draw(ctx, renderer->vertex_count, 0);
}
}
void block_renderer_add_quad(BlockRenderer* renderer, float x, float y, float w, float h, float r, float g, float b, float a) {
if (renderer->vertex_count + 4 > renderer->vertex_capacity) return;
if (renderer->index_count + 6 > renderer->index_capacity) return;
uint32_t base = renderer->vertex_count;
float* v = renderer->vertex_data + base * 7;
v[0*7+0] = x; v[0*7+1] = y; v[0*7+2] = 0; v[0*7+3] = r; v[0*7+4] = g; v[0*7+5] = b; v[0*7+6] = a;
v[1*7+0] = x + w; v[1*7+1] = y; v[1*7+2] = 0; v[1*7+3] = r; v[1*7+4] = g; v[1*7+5] = b; v[1*7+6] = a;
v[2*7+0] = x + w; v[2*7+1] = y + h; v[2*7+2] = 0; v[2*7+3] = r; v[2*7+4] = g; v[2*7+5] = b; v[2*7+6] = a;
v[3*7+0] = x; v[3*7+1] = y + h; v[3*7+2] = 0; v[3*7+3] = r; v[3*7+4] = g; v[3*7+5] = b; v[3*7+6] = a;
renderer->vertex_count += 4;
renderer->index_data[renderer->index_count++] = base + 0;
renderer->index_data[renderer->index_count++] = base + 1;
renderer->index_data[renderer->index_count++] = base + 2;
renderer->index_data[renderer->index_count++] = base + 0;
renderer->index_data[renderer->index_count++] = base + 2;
renderer->index_data[renderer->index_count++] = base + 3;
}
void block_renderer_add_line(BlockRenderer* renderer, float x1, float y1, float x2, float y2, float r, float g, float b, float a) {
float dx = x2 - x1;
float dy = y2 - y1;
float len = sqrtf(dx * dx + dy * dy);
if (len < 0.001f) return;
const float thickness = 1.0f;
float nx = -dy / len * thickness * 0.5f;
float ny = dx / len * thickness * 0.5f;
if (renderer->vertex_count + 4 > renderer->vertex_capacity) return;
if (renderer->index_count + 6 > renderer->index_capacity) return;
uint32_t base = renderer->vertex_count;
float* v = renderer->vertex_data + base * 7;
v[0*7+0] = x1 + nx; v[0*7+1] = y1 + ny; v[0*7+2] = 0; v[0*7+3] = r; v[0*7+4] = g; v[0*7+5] = b; v[0*7+6] = a;
v[1*7+0] = x2 + nx; v[1*7+1] = y2 + ny; v[1*7+2] = 0; v[1*7+3] = r; v[1*7+4] = g; v[1*7+5] = b; v[1*7+6] = a;
v[2*7+0] = x2 - nx; v[2*7+1] = y2 - ny; v[2*7+2] = 0; v[2*7+3] = r; v[2*7+4] = g; v[2*7+5] = b; v[2*7+6] = a;
v[3*7+0] = x1 - nx; v[3*7+1] = y1 - ny; v[3*7+2] = 0; v[3*7+3] = r; v[3*7+4] = g; v[3*7+5] = b; v[3*7+6] = a;
renderer->vertex_count += 4;
renderer->index_data[renderer->index_count++] = base + 0;
renderer->index_data[renderer->index_count++] = base + 1;
renderer->index_data[renderer->index_count++] = base + 2;
renderer->index_data[renderer->index_count++] = base + 0;
renderer->index_data[renderer->index_count++] = base + 2;
renderer->index_data[renderer->index_count++] = base + 3;
}
static void block_renderer_add_bezier(BlockRenderer* renderer, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float r, float g, float b, float a) {
int segments = 16;
float prev_x = x1, prev_y = y1;
for (int i = 1; i <= segments; i++) {
float t = (float)i / segments;
float mt = 1.0f - t;
float px = mt*mt*mt*x1 + 3*mt*mt*t*cx1 + 3*mt*t*t*cx2 + t*t*t*x2;
float py = mt*mt*mt*y1 + 3*mt*mt*t*cy1 + 3*mt*t*t*cy2 + t*t*t*y2;
block_renderer_add_line(renderer, prev_x, prev_y, px, py, r, g, b, a);
prev_x = px;
prev_y = py;
}
}
void block_renderer_draw_block(BlockRenderer* renderer, Block* block, float view_x, float view_y, float zoom) {
float x = (block->x - view_x) * zoom;
float y = (block->y - view_y) * zoom;
float w = block->width * zoom;
float h = block->height * zoom;
block_renderer_add_quad(renderer, x, y, w, h, 0.3f, 0.3f, 0.35f, 1.0f);
block_renderer_add_quad(renderer, x, y, w, 20 * zoom, 0.2f, 0.2f, 0.25f, 1.0f);
if (block->label[0]) {
block_renderer_draw_text(renderer, block->label, x + 4.0f * zoom, y + 4.0f * zoom, 0.9f, 0.9f, 0.9f);
}
}
void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, BlockSlot* to, float view_x, float view_y, float zoom) {
float x1 = (from->owner->x + from->x - view_x) * zoom;
float y1 = (from->owner->y + from->y - view_y) * zoom;
float x2 = (to->owner->x + to->x - view_x) * zoom;
float y2 = (to->owner->y + to->y - view_y) * zoom;
float dx = fabsf(x2 - x1) * 0.5f;
if (dx < 50.0f) dx = 50.0f;
block_renderer_add_bezier(renderer, x1, y1, x1 + dx, y1, x2 - dx, y2, x2, y2, 0.8f, 0.8f, 0.2f, 1.0f);
}
void block_renderer_draw_text(BlockRenderer* renderer, const char* text, float x, float y, float r, float g, float b) {
if (!text) return;
const float scale = 2.0f;
const float advance = 5.0f * scale + 1.0f;
float cx = x;
for (const char* p = text; *p; ++p) {
unsigned char ch = (unsigned char)*p;
if (ch < 32 || ch > 127) ch = '?';
int idx = ch - 32;
for (int col = 0; col < 5; ++col) {
uint8_t column = BLOCK_FONT[idx][col];
for (int row = 0; row < 7; ++row) {
if (column & (1u << row)) {
block_renderer_add_quad(renderer,
cx + col * scale,
y + row * scale,
scale, scale,
r, g, b, 1.0f);
}
}
}
cx += advance;
}
}
@@ -0,0 +1,32 @@
#pragma once
#include "block_workspace.h"
#include <d3d11.h>
#include <stdint.h>
typedef struct BlockRenderer {
ID3D11Device* device;
ID3D11DeviceContext* context;
ID3D11Buffer* vertex_buffer;
ID3D11Buffer* index_buffer;
ID3D11VertexShader* vertex_shader;
ID3D11PixelShader* pixel_shader;
ID3D11InputLayout* input_layout;
float* vertex_data;
int vertex_count;
int vertex_capacity;
uint32_t* index_data;
int index_count;
int index_capacity;
} BlockRenderer;
int block_renderer_init(BlockRenderer* renderer, void* device, void* context);
void block_renderer_shutdown(BlockRenderer* renderer);
void block_renderer_begin(BlockRenderer* renderer);
void block_renderer_end(BlockRenderer* renderer);
void block_renderer_draw_block(BlockRenderer* renderer, Block* block, float view_x, float view_y, float zoom);
void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, BlockSlot* to, float view_x, float view_y, float zoom);
void block_renderer_draw_text(BlockRenderer* renderer, const char* text, float x, float y, float r, float g, float b);
void block_renderer_add_quad(BlockRenderer* renderer, float x, float y, float w, float h, float r, float g, float b, float a);
void block_renderer_add_line(BlockRenderer* renderer, float x1, float y1, float x2, float y2, float r, float g, float b, float a);
@@ -0,0 +1,415 @@
// ============================================================================
// block_serializer.c - JSON 序列化实现
// ============================================================================
#include "block_serializer.h"
#include "block_types.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 简单的 JSON 生成器
typedef struct {
char* buffer;
size_t size;
size_t capacity;
} JsonBuffer;
static void json_init(JsonBuffer* buf) {
buf->capacity = 4096;
buf->buffer = (char*)malloc(buf->capacity);
buf->size = 0;
buf->buffer[0] = '\0';
}
static void json_append(JsonBuffer* buf, const char* str) {
size_t len = strlen(str);
if (buf->size + len + 1 > buf->capacity) {
buf->capacity = (buf->size + len + 1) * 2;
buf->buffer = (char*)realloc(buf->buffer, buf->capacity);
}
memcpy(buf->buffer + buf->size, str, len + 1);
buf->size += len;
}
int serializer_save(BlockWorkspace* ws, const char* filepath) {
char* json = serializer_save_to_string(ws);
if (!json) return 0;
FILE* f = fopen(filepath, "w");
if (!f) {
free(json);
return 0;
}
fprintf(f, "%s", json);
fclose(f);
free(json);
return 1;
}
int serializer_load(BlockWorkspace* ws, const char* filepath) {
FILE* f = fopen(filepath, "r");
if (!f) return 0;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
char* json = (char*)malloc(size + 1);
fread(json, 1, size, f);
json[size] = '\0';
fclose(f);
int result = serializer_load_from_string(ws, json);
free(json);
return result;
}
char* serializer_save_to_string(BlockWorkspace* ws) {
JsonBuffer buf;
json_init(&buf);
json_append(&buf, "{\n");
json_append(&buf, " \"version\": \"1.0\",\n");
// 保存积木
json_append(&buf, " \"blocks\": [\n");
for (int i = 0; i < ws->block_count; i++) {
Block* block = ws->blocks[i];
const BlockTypeInfo* info = block_type_get_info(block->type);
char tmp[256];
snprintf(tmp, 256, " {\n");
json_append(&buf, tmp);
snprintf(tmp, 256, " \"id\": %d,\n", block->id);
json_append(&buf, tmp);
snprintf(tmp, 256, " \"type\": \"%s\",\n", info ? info->name : "unknown");
json_append(&buf, tmp);
snprintf(tmp, 256, " \"x\": %.1f,\n", block->x);
json_append(&buf, tmp);
snprintf(tmp, 256, " \"y\": %.1f,\n", block->y);
json_append(&buf, tmp);
snprintf(tmp, 256, " \"label\": \"%s\"", block->label);
json_append(&buf, tmp);
// 保存输入槽默认值
if (block->input_count > 0) {
json_append(&buf, ",\n \"inputs\": [\n");
for (int j = 0; j < block->input_count; j++) {
BlockSlot* slot = &block->inputs[j];
SlotValue* val = &slot->value;
switch (val->type) {
case SLOT_VALUE_NUMBER:
snprintf(tmp, 256, " {\"type\": \"number\", \"value\": %.6g}%s\n", val->as.number, j < block->input_count - 1 ? "," : "");
break;
case SLOT_VALUE_STRING:
snprintf(tmp, 256, " {\"type\": \"string\", \"value\": \"%s\"}%s\n", val->as.string, j < block->input_count - 1 ? "," : "");
break;
case SLOT_VALUE_BOOLEAN:
snprintf(tmp, 256, " {\"type\": \"boolean\", \"value\": %s}%s\n", val->as.boolean ? "true" : "false", j < block->input_count - 1 ? "," : "");
break;
default:
snprintf(tmp, 256, " {\"type\": \"number\", \"value\": 0}%s\n", j < block->input_count - 1 ? "," : "");
break;
}
json_append(&buf, tmp);
}
json_append(&buf, " ]");
}
json_append(&buf, "\n");
json_append(&buf, i < ws->block_count - 1 ? " },\n" : " }\n");
}
json_append(&buf, " ],\n");
// 保存连接
json_append(&buf, " \"connections\": [\n");
for (int i = 0; i < ws->connection_count; i++) {
BlockConnection* conn = &ws->connections[i];
char tmp[128];
snprintf(tmp, 128, " {\"from\": %d, \"to\": %d}%s\n",
conn->from ? conn->from->owner->id : 0,
conn->to ? conn->to->owner->id : 0,
i < ws->connection_count - 1 ? "," : "");
json_append(&buf, tmp);
}
json_append(&buf, " ]\n");
json_append(&buf, "}\n");
return buf.buffer;
}
// 跳过空白字符
static const char* skip_ws(const char* p) {
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
return p;
}
// 查找字符串值,返回指向引号内内容的指针,并在 *len 中设置长度
static const char* parse_json_string(const char* p, int* len) {
p = skip_ws(p);
if (*p != '"') return NULL;
p++;
const char* start = p;
while (*p && *p != '"') p++;
if (*p != '"') return NULL;
*len = (int)(p - start);
return start;
}
// 查找数字值,返回指向数字开头的指针
static const char* parse_json_number(const char* p, float* out) {
p = skip_ws(p);
const char* start = p;
if (*p == '-') p++;
while (*p >= '0' && *p <= '9') p++;
if (*p == '.') {
p++;
while (*p >= '0' && *p <= '9') p++;
}
if (p == start) return NULL;
*out = (float)strtod(start, NULL);
return p;
}
int serializer_load_from_string(BlockWorkspace* ws, const char* json_str) {
workspace_clear(ws);
block_types_init();
// 解析 blocks 数组
const char* blocks_key = strstr(json_str, "\"blocks\"");
if (!blocks_key) return 0;
const char* p = blocks_key + 8;
p = skip_ws(p);
if (*p != ':') return 0;
p++;
p = skip_ws(p);
if (*p != '[') return 0;
p++;
while (1) {
p = skip_ws(p);
if (*p == ']') { p++; break; }
if (*p != '{') return 0;
p++;
int id = 0;
char type_name[64] = {0};
char label[64] = {0};
float x = 0, y = 0;
SlotValue input_values[MAX_BLOCK_INPUTS];
int input_count = 0;
while (1) {
p = skip_ws(p);
if (*p == '}') { p++; break; }
int key_len = 0;
const char* key = parse_json_string(p, &key_len);
if (!key) return 0;
p = key + key_len + 1;
p = skip_ws(p);
if (*p != ':') return 0;
p++;
if (key_len == 2 && strncmp(key, "id", 2) == 0) {
float fv; p = parse_json_number(p, &fv); id = (int)fv;
} else if (key_len == 4 && strncmp(key, "type", 4) == 0) {
int val_len = 0;
const char* val = parse_json_string(p, &val_len);
if (val) {
if (val_len >= (int)sizeof(type_name)) val_len = (int)sizeof(type_name) - 1;
strncpy(type_name, val, val_len);
type_name[val_len] = '\0';
p = val + val_len + 1;
}
} else if (key_len == 1 && *key == 'x') {
p = parse_json_number(p, &x);
} else if (key_len == 1 && *key == 'y') {
p = parse_json_number(p, &y);
} else if (key_len == 5 && strncmp(key, "label", 5) == 0) {
int val_len = 0;
const char* val = parse_json_string(p, &val_len);
if (val) {
if (val_len >= (int)sizeof(label)) val_len = (int)sizeof(label) - 1;
strncpy(label, val, val_len);
label[val_len] = '\0';
p = val + val_len + 1;
}
} else if (key_len == 6 && strncmp(key, "inputs", 6) == 0) {
p = skip_ws(p);
if (*p == '[') {
p++;
input_count = 0;
while (1) {
p = skip_ws(p);
if (*p == ']') { p++; break; }
if (*p != '{') break;
p++;
char iv_type[16] = {0};
float fv = 0;
int bv = 0;
char sval_str[256] = {0};
while (1) {
p = skip_ws(p);
if (*p == '}') { p++; break; }
int ik_len = 0;
const char* ik = parse_json_string(p, &ik_len);
if (!ik) break;
p = ik + ik_len + 1;
p = skip_ws(p);
if (*p != ':') break;
p++;
if (ik_len == 4 && strncmp(ik, "type", 4) == 0) {
int iv_len = 0;
const char* iv = parse_json_string(p, &iv_len);
if (iv) {
if (iv_len >= (int)sizeof(iv_type)) iv_len = (int)sizeof(iv_type) - 1;
strncpy(iv_type, iv, iv_len);
iv_type[iv_len] = '\0';
p = iv + iv_len + 1;
}
} else if (ik_len == 5 && strncmp(ik, "value", 5) == 0) {
p = skip_ws(p);
if (*p == '"') {
int sv_len = 0;
const char* sval = parse_json_string(p, &sv_len);
if (sval) {
if (sv_len >= (int)sizeof(sval_str)) sv_len = (int)sizeof(sval_str) - 1;
strncpy(sval_str, sval, sv_len);
sval_str[sv_len] = '\0';
p = sval + sv_len + 1;
}
} else if (strncmp(p, "true", 4) == 0) {
bv = 1; p += 4;
} else if (strncmp(p, "false", 5) == 0) {
bv = 0; p += 5;
} else {
p = parse_json_number(p, &fv);
}
} else {
while (*p && *p != ',' && *p != '}') p++;
}
p = skip_ws(p);
if (*p == ',') p++;
}
// 暂存输入值
if (input_count < MAX_BLOCK_INPUTS) {
SlotValue* sv = &input_values[input_count++];
memset(sv, 0, sizeof(SlotValue));
if (strcmp(iv_type, "number") == 0) {
sv->type = SLOT_VALUE_NUMBER;
sv->as.number = (double)fv;
} else if (strcmp(iv_type, "string") == 0) {
sv->type = SLOT_VALUE_STRING;
strncpy(sv->as.string, sval_str, sizeof(sv->as.string) - 1);
} else if (strcmp(iv_type, "boolean") == 0) {
sv->type = SLOT_VALUE_BOOLEAN;
sv->as.boolean = bv;
} else {
sv->type = SLOT_VALUE_NUMBER;
sv->as.number = 0;
}
}
p = skip_ws(p);
if (*p == ',') p++;
}
}
} else {
// 跳过未知值 (简单跳过直到逗号或右花括号)
while (*p && *p != ',' && *p != '}') p++;
}
p = skip_ws(p);
if (*p == ',') p++;
}
BlockType type = block_type_find_by_name(type_name);
if (type == BLOCK_TYPE_COUNT || type_name[0] == '\0') {
type = BLOCK_EVENT_START; // fallback
}
Block* block = block_create(type, x, y);
if (block) {
block->id = id;
strncpy(block->label, label, sizeof(block->label) - 1);
block->label[sizeof(block->label) - 1] = '\0';
for (int j = 0; j < input_count && j < block->input_count; j++) {
block->inputs[j].value = input_values[j];
}
workspace_add_block(ws, block);
if (id >= ws->next_block_id) ws->next_block_id = id + 1;
}
p = skip_ws(p);
if (*p == ',') p++;
}
// 解析 connections 数组
const char* conn_key = strstr(json_str, "\"connections\"");
if (conn_key) {
p = conn_key + 13;
p = skip_ws(p);
if (*p == ':') {
p++;
p = skip_ws(p);
if (*p == '[') {
p++;
while (1) {
p = skip_ws(p);
if (*p == ']') { p++; break; }
if (*p != '{') break;
p++;
int from_id = -1, to_id = -1;
while (1) {
p = skip_ws(p);
if (*p == '}') { p++; break; }
int key_len = 0;
const char* key = parse_json_string(p, &key_len);
if (!key) break;
p = key + key_len + 1;
p = skip_ws(p);
if (*p != ':') break;
p++;
float fv = 0;
if (key_len == 4 && strncmp(key, "from", 4) == 0) {
p = parse_json_number(p, &fv); from_id = (int)fv;
} else if (key_len == 2 && strncmp(key, "to", 2) == 0) {
p = parse_json_number(p, &fv); to_id = (int)fv;
} else {
while (*p && *p != ',' && *p != '}') p++;
}
p = skip_ws(p);
if (*p == ',') p++;
}
if (from_id >= 0 && to_id >= 0) {
Block* from_block = workspace_find_block(ws, from_id);
Block* to_block = workspace_find_block(ws, to_id);
if (from_block && to_block && from_block->output_count > 0 && to_block->input_count > 0) {
workspace_add_connection(ws, &from_block->outputs[0], &to_block->inputs[0]);
}
}
p = skip_ws(p);
if (*p == ',') p++;
}
}
}
}
return 1;
}
@@ -0,0 +1,19 @@
#pragma once
// ============================================================================
// block_serializer.h - JSON 序列化
// ============================================================================
#include "block_workspace.h"
// 保存工作区到文件
int serializer_save(BlockWorkspace* ws, const char* filepath);
// 从文件加载工作区
int serializer_load(BlockWorkspace* ws, const char* filepath);
// 保存到字符串
char* serializer_save_to_string(BlockWorkspace* ws);
// 从字符串加载
int serializer_load_from_string(BlockWorkspace* ws, const char* json_str);
+326
View File
@@ -0,0 +1,326 @@
// ============================================================================
// block_types.c - 积木类型注册
// ============================================================================
#include "block_types.h"
#include <string.h>
static BlockTypeInfo g_type_info[BLOCK_TYPE_COUNT];
static int g_types_initialized = 0;
void block_types_init(void) {
if (g_types_initialized) return;
int i = 0;
// === 事件积木 (红色) ===
g_type_info[i++] = (BlockTypeInfo){
BLOCK_EVENT_START, "event_start", "on_start", "Events",
BLOCK_COLOR_EVENT, 0, 1,
{}, {SLOT_STATEMENT},
{}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_EVENT_UPDATE, "event_update", "on_update", "Events",
BLOCK_COLOR_EVENT, 0, 1,
{}, {SLOT_STATEMENT},
{}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_EVENT_COLLISION, "event_collision", "on_collision", "Events",
BLOCK_COLOR_EVENT, 1, 1,
{SLOT_VALUE_STRING}, {SLOT_STATEMENT},
{"碰撞对象"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_EVENT_KEY_PRESSED, "event_key", "on_key_pressed", "Events",
BLOCK_COLOR_EVENT, 1, 1,
{SLOT_VALUE_STRING}, {SLOT_STATEMENT},
{"按键"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_EVENT_MOUSE_CLICK, "event_mouse", "on_mouse_click", "Events",
BLOCK_COLOR_EVENT, 0, 1,
{}, {SLOT_STATEMENT},
{}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_EVENT_TIMER, "event_timer", "on_timer", "Events",
BLOCK_COLOR_EVENT, 1, 1,
{SLOT_VALUE_NUMBER}, {SLOT_STATEMENT},
{"间隔(秒)"}, {"执行"}
};
// === 动作积木 (蓝色) ===
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_MOVE, "action_move", "move", "Actions",
BLOCK_COLOR_ACTION, 4, 1,
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_STATEMENT},
{"执行", "X", "Y", "Z"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_ROTATE, "action_rotate", "rotate", "Actions",
BLOCK_COLOR_ACTION, 4, 1,
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_STATEMENT},
{"执行", "X", "Y", "Z"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_SCALE, "action_scale", "scale", "Actions",
BLOCK_COLOR_ACTION, 4, 1,
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_STATEMENT},
{"执行", "X", "Y", "Z"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_SET_POSITION, "action_setpos", "set_position", "Actions",
BLOCK_COLOR_ACTION, 4, 1,
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_STATEMENT},
{"执行", "X", "Y", "Z"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_SET_ROTATION, "action_setrot", "set_rotation", "Actions",
BLOCK_COLOR_ACTION, 4, 1,
{SLOT_STATEMENT, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_STATEMENT},
{"执行", "X", "Y", "Z"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_PLAY_SOUND, "action_sound", "play_sound", "Actions",
BLOCK_COLOR_ACTION, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_STRING},
{SLOT_STATEMENT},
{"执行", "音效名"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_DESTROY, "action_destroy", "destroy", "Actions",
BLOCK_COLOR_ACTION, 1, 1,
{SLOT_STATEMENT},
{SLOT_STATEMENT},
{"执行"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_ACTION_INSTANTIATE, "action_instantiate", "instantiate", "Actions",
BLOCK_COLOR_ACTION, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_STRING},
{SLOT_STATEMENT},
{"执行", "预制体"}, {"执行"}
};
// === 控制积木 (绿色) ===
g_type_info[i++] = (BlockTypeInfo){
BLOCK_CONTROL_IF, "control_if", "if", "Control",
BLOCK_COLOR_CONTROL, 2, 2,
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
{SLOT_STATEMENT, SLOT_STATEMENT},
{"执行", "条件"}, {"Then", "Else"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_CONTROL_IF_ELSE, "control_ifelse", "if_else", "Control",
BLOCK_COLOR_CONTROL, 2, 2,
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
{SLOT_STATEMENT, SLOT_STATEMENT},
{"执行", "条件"}, {"Then", "Else"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_CONTROL_REPEAT, "control_repeat", "repeat", "Control",
BLOCK_COLOR_CONTROL, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
{SLOT_STATEMENT},
{"执行", "条件"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_CONTROL_REPEAT_TIMES, "control_count", "repeat_times", "Control",
BLOCK_COLOR_CONTROL, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_NUMBER},
{SLOT_STATEMENT},
{"执行", "次数"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_CONTROL_WHILE, "control_while", "while", "Control",
BLOCK_COLOR_CONTROL, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_BOOLEAN},
{SLOT_STATEMENT},
{"执行", "条件"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_CONTROL_WAIT, "control_wait", "wait", "Control",
BLOCK_COLOR_CONTROL, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_NUMBER},
{SLOT_STATEMENT},
{"执行", "秒数"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_CONTROL_FUNCTION_DEF, "control_funcdef", "function_def", "Control",
BLOCK_COLOR_CONTROL, 1, 1,
{SLOT_STATEMENT},
{SLOT_STATEMENT},
{"执行"}, {"执行"}
};
// === 运算积木 (橙色) ===
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_ADD, "op_add", "+", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_VALUE_NUMBER},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_SUB, "op_sub", "-", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_VALUE_NUMBER},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_MUL, "op_mul", "*", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_VALUE_NUMBER},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_DIV, "op_div", "/", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_VALUE_NUMBER},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_MOD, "op_mod", "%", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_VALUE_NUMBER},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_EQUALS, "op_eq", "==", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_ANY, SLOT_VALUE_ANY},
{SLOT_VALUE_BOOLEAN},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_NOT_EQUALS, "op_neq", "!=", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_ANY, SLOT_VALUE_ANY},
{SLOT_VALUE_BOOLEAN},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_GREATER, "op_gt", ">", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_VALUE_BOOLEAN},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_LESS, "op_lt", "<", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_NUMBER, SLOT_VALUE_NUMBER},
{SLOT_VALUE_BOOLEAN},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_AND, "op_and", "AND", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_BOOLEAN, SLOT_VALUE_BOOLEAN},
{SLOT_VALUE_BOOLEAN},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_OR, "op_or", "OR", "Operators",
BLOCK_COLOR_OPERATOR, 2, 1,
{SLOT_VALUE_BOOLEAN, SLOT_VALUE_BOOLEAN},
{SLOT_VALUE_BOOLEAN},
{"A", "B"}, {"结果"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_OP_NOT, "op_not", "NOT", "Operators",
BLOCK_COLOR_OPERATOR, 1, 1,
{SLOT_VALUE_BOOLEAN},
{SLOT_VALUE_BOOLEAN},
{"A"}, {"结果"}
};
// === 变量积木 (紫色) ===
g_type_info[i++] = (BlockTypeInfo){
BLOCK_VAR_GET, "var_get", "get_variable", "Variables",
BLOCK_COLOR_VARIABLE, 0, 1,
{}, {SLOT_VALUE_ANY},
{}, {"值"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_VAR_SET, "var_set", "set_variable", "Variables",
BLOCK_COLOR_VARIABLE, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_ANY},
{SLOT_STATEMENT},
{"执行", "值"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_VAR_CREATE, "var_create", "create_variable", "Variables",
BLOCK_COLOR_VARIABLE, 1, 1,
{SLOT_STATEMENT},
{SLOT_STATEMENT},
{"执行"}, {"执行"}
};
// === 函数积木 (黄色) ===
g_type_info[i++] = (BlockTypeInfo){
BLOCK_FUNC_CALL, "func_call", "call_function", "Functions",
BLOCK_COLOR_FUNCTION, 2, 1,
{SLOT_STATEMENT, SLOT_VALUE_STRING},
{SLOT_STATEMENT},
{"执行", "函数名"}, {"执行"}
};
g_type_info[i++] = (BlockTypeInfo){
BLOCK_FUNC_RETURN, "func_return", "return", "Functions",
BLOCK_COLOR_FUNCTION, 2, 0,
{SLOT_STATEMENT, SLOT_VALUE_ANY},
{},
{"执行", "返回值"}
};
g_types_initialized = 1;
}
const BlockTypeInfo* block_type_get_info(BlockType type) {
if (type < 0 || type >= BLOCK_TYPE_COUNT) return NULL;
return &g_type_info[type];
}
const BlockTypeInfo* block_type_get_all(int* count) {
*count = BLOCK_TYPE_COUNT;
return g_type_info;
}
BlockType block_type_find_by_name(const char* name) {
for (int i = 0; i < BLOCK_TYPE_COUNT; i++) {
if (strcmp(g_type_info[i].name, name) == 0) {
return g_type_info[i].type;
}
}
return -1;
}
const char** block_type_get_categories(int* count) {
static const char* categories[] = {
"Events", "Actions", "Control", "Operators", "Variables", "Functions"
};
*count = 6;
return categories;
}
const BlockType* block_type_get_by_category(const char* category, int* count) {
static BlockType buffer[BLOCK_TYPE_COUNT];
*count = 0;
for (int i = 0; i < BLOCK_TYPE_COUNT; i++) {
if (strcmp(g_type_info[i].category, category) == 0) {
buffer[(*count)++] = g_type_info[i].type;
}
}
return buffer;
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
// ============================================================================
// block_types.h - 积木类型定义
// ============================================================================
#include "block_core.h"
// 获取积木类型信息
const BlockTypeInfo* block_type_get_info(BlockType type);
// 获取所有类型信息
const BlockTypeInfo* block_type_get_all(int* count);
// 按名称查找类型
BlockType block_type_find_by_name(const char* name);
// 按分类获取类型列表
const BlockType* block_type_get_by_category(const char* category, int* count);
// 获取所有分类
const char** block_type_get_categories(int* count);
// 初始化所有积木类型
void block_types_init(void);
@@ -0,0 +1,171 @@
// ============================================================================
// block_workspace.c - 工作区管理实现
// ============================================================================
#include "block_workspace.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
BlockWorkspace* workspace_create(void) {
BlockWorkspace* ws = (BlockWorkspace*)calloc(1, sizeof(BlockWorkspace));
if (!ws) return NULL;
ws->zoom = 1.0f;
ws->next_block_id = 1;
return ws;
}
void workspace_destroy(BlockWorkspace* ws) {
if (!ws) return;
workspace_clear(ws);
free(ws);
}
int workspace_add_block(BlockWorkspace* ws, Block* block) {
if (ws->block_count >= MAX_WORKSPACE_BLOCKS) return -1;
if (!block) return -1;
if (block->id <= 0) block->id = ws->next_block_id++;
else if (block->id >= ws->next_block_id) ws->next_block_id = block->id + 1;
block->workspace = ws;
ws->blocks[ws->block_count++] = block;
return block->id;
}
void workspace_remove_block(BlockWorkspace* ws, int block_id) {
// 先移除所有相关连接
for (int i = ws->connection_count - 1; i >= 0; i--) {
BlockSlot* from = ws->connections[i].from;
BlockSlot* to = ws->connections[i].to;
if (from && from->owner && from->owner->id == block_id) {
workspace_remove_connection(ws, from);
} else if (to && to->owner && to->owner->id == block_id) {
workspace_remove_connection(ws, to);
}
}
// 移除积木
for (int i = 0; i < ws->block_count; i++) {
if (ws->blocks[i]->id == block_id) {
block_destroy(ws->blocks[i]);
ws->blocks[i] = ws->blocks[ws->block_count - 1];
ws->block_count--;
return;
}
}
}
bool workspace_add_connection(BlockWorkspace* ws, BlockSlot* from, BlockSlot* to) {
if (ws->connection_count >= MAX_WORKSPACE_CONNECTIONS) return false;
if (!from || !to) return false;
// 检查是否已连接
for (int i = 0; i < ws->connection_count; i++) {
if (ws->connections[i].from == from || ws->connections[i].to == to) {
return false; // 已有连接
}
}
// 建立连接
block_connect(from, to);
BlockConnection* conn = &ws->connections[ws->connection_count++];
conn->from = from;
conn->to = to;
return true;
}
void workspace_remove_connection(BlockWorkspace* ws, BlockSlot* slot) {
for (int i = ws->connection_count - 1; i >= 0; i--) {
if (ws->connections[i].from == slot || ws->connections[i].to == slot) {
block_disconnect(ws->connections[i].from);
ws->connections[i] = ws->connections[ws->connection_count - 1];
ws->connection_count--;
}
}
}
void workspace_remove_all_connections(BlockWorkspace* ws, Block* block) {
if (!block) return;
for (int i = ws->connection_count - 1; i >= 0; i--) {
BlockSlot* from = ws->connections[i].from;
BlockSlot* to = ws->connections[i].to;
if ((from && from->owner == block) || (to && to->owner == block)) {
block_disconnect(from);
ws->connections[i] = ws->connections[ws->connection_count - 1];
ws->connection_count--;
}
}
}
Block* workspace_find_block(BlockWorkspace* ws, int block_id) {
for (int i = 0; i < ws->block_count; i++) {
if (ws->blocks[i]->id == block_id) return ws->blocks[i];
}
return NULL;
}
Block* workspace_find_block_at(BlockWorkspace* ws, float x, float y) {
// 从后往前找 (最后添加的在最上面)
for (int i = ws->block_count - 1; i >= 0; i--) {
if (block_contains_point(ws->blocks[i], x, y)) {
return ws->blocks[i];
}
}
return NULL;
}
BlockSlot* workspace_find_slot_at(BlockWorkspace* ws, float x, float y) {
float threshold = 10.0f;
for (int i = ws->block_count - 1; i >= 0; i--) {
Block* block = ws->blocks[i];
// 检查输出槽位
for (int j = 0; j < block->output_count; j++) {
BlockSlot* slot = &block->outputs[j];
float sx, sy;
block_slot_world_pos(slot, &sx, &sy);
float dx = x - sx;
float dy = y - sy;
if (dx * dx + dy * dy < threshold * threshold) {
return slot;
}
}
// 检查输入槽位
for (int j = 0; j < block->input_count; j++) {
BlockSlot* slot = &block->inputs[j];
float sx, sy;
block_slot_world_pos(slot, &sx, &sy);
float dx = x - sx;
float dy = y - sy;
if (dx * dx + dy * dy < threshold * threshold) {
return slot;
}
}
}
return NULL;
}
void workspace_screen_to_world(BlockWorkspace* ws, float sx, float sy, float* wx, float* wy) {
*wx = (sx / ws->zoom) + ws->view_x;
*wy = (sy / ws->zoom) + ws->view_y;
}
void workspace_world_to_screen(BlockWorkspace* ws, float wx, float wy, float* sx, float* sy) {
*sx = (wx - ws->view_x) * ws->zoom;
*sy = (wy - ws->view_y) * ws->zoom;
}
void workspace_clear(BlockWorkspace* ws) {
for (int i = 0; i < ws->block_count; i++) {
block_destroy(ws->blocks[i]);
}
ws->block_count = 0;
ws->connection_count = 0;
ws->selected_block = NULL;
}
@@ -0,0 +1,54 @@
#pragma once
// ============================================================================
// block_workspace.h - 工作区管理
// ============================================================================
#include "block_core.h"
#define MAX_WORKSPACE_BLOCKS 1024
#define MAX_WORKSPACE_CONNECTIONS 2048
typedef struct BlockWorkspace {
Block* blocks[MAX_WORKSPACE_BLOCKS];
int block_count;
BlockConnection connections[MAX_WORKSPACE_CONNECTIONS];
int connection_count;
float view_x, view_y;
float zoom;
Block* selected_block;
BlockSlot* dragging_from;
float drag_start_x, drag_start_y;
int next_block_id;
struct BlockPalette* palette;
} BlockWorkspace;
// 创建/销毁
BlockWorkspace* workspace_create(void);
void workspace_destroy(BlockWorkspace* ws);
// 添加/移除积木
int workspace_add_block(BlockWorkspace* ws, Block* block);
void workspace_remove_block(BlockWorkspace* ws, int block_id);
// 添加/移除连接
bool workspace_add_connection(BlockWorkspace* ws, BlockSlot* from, BlockSlot* to);
void workspace_remove_connection(BlockWorkspace* ws, BlockSlot* from);
void workspace_remove_all_connections(BlockWorkspace* ws, Block* block);
// 查找
Block* workspace_find_block(BlockWorkspace* ws, int block_id);
Block* workspace_find_block_at(BlockWorkspace* ws, float x, float y);
BlockSlot* workspace_find_slot_at(BlockWorkspace* ws, float x, float y);
// 视口变换
void workspace_screen_to_world(BlockWorkspace* ws, float sx, float sy, float* wx, float* wy);
void workspace_world_to_screen(BlockWorkspace* ws, float wx, float wy, float* sx, float* sy);
// 清空
void workspace_clear(BlockWorkspace* ws);
+198
View File
@@ -0,0 +1,198 @@
// ============================================================================
// window.c - Win32 窗口实现
// ============================================================================
#include "window.h"
#include "../Core/logger.h"
#include <stdlib.h>
#include <string.h>
// 窗口类名
static const wchar_t* WINDOW_CLASS = L"Lity3DEngineWindow";
// 全局回调
static WindowKeyCallback g_key_callback = NULL;
static WindowResizeCallback g_resize_callback = NULL;
static WindowMouseMoveCallback g_mouse_move_callback = NULL;
static WindowMouseButtonCallback g_mouse_button_callback = NULL;
// 窗口过程
static LRESULT CALLBACK window_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
Window* window = NULL;
if (msg == WM_NCCREATE) {
CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
window = (Window*)cs->lpCreateParams;
SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONG_PTR)window);
} else {
window = (Window*)GetWindowLongPtrW(hwnd, GWLP_USERDATA);
}
if (window) {
switch (msg) {
case WM_SIZE:
window->width = LOWORD(lParam);
window->height = HIWORD(lParam);
window->minimized = (wParam == SIZE_MINIMIZED);
if (g_resize_callback && !window->minimized) {
g_resize_callback(window->width, window->height);
}
return 0;
case WM_PAINT:
ValidateRect(hwnd, NULL);
return 0;
case WM_ERASEBKGND:
return 1;
case WM_KEYDOWN:
if (g_key_callback) g_key_callback((int)wParam);
return 0;
case WM_KEYUP:
return 0;
case WM_MOUSEMOVE:
if (g_mouse_move_callback) g_mouse_move_callback(LOWORD(lParam), HIWORD(lParam));
return 0;
case WM_LBUTTONDOWN:
SetCapture(hwnd);
if (g_mouse_button_callback) g_mouse_button_callback(0, 1);
return 0;
case WM_RBUTTONDOWN:
SetCapture(hwnd);
if (g_mouse_button_callback) g_mouse_button_callback(1, 1);
return 0;
case WM_MBUTTONDOWN:
SetCapture(hwnd);
if (g_mouse_button_callback) g_mouse_button_callback(2, 1);
return 0;
case WM_LBUTTONUP:
if (GetCapture() == hwnd) ReleaseCapture();
if (g_mouse_button_callback) g_mouse_button_callback(0, 0);
return 0;
case WM_RBUTTONUP:
if (GetCapture() == hwnd) ReleaseCapture();
if (g_mouse_button_callback) g_mouse_button_callback(1, 0);
return 0;
case WM_MBUTTONUP:
if (GetCapture() == hwnd) ReleaseCapture();
if (g_mouse_button_callback) g_mouse_button_callback(2, 0);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
// 注册窗口类
static void register_window_class(void) {
WNDCLASSEXW wc = {0};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = window_proc;
wc.hInstance = GetModuleHandleW(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszClassName = WINDOW_CLASS;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassExW(&wc)) {
LOG_ERROR("Failed to register window class!");
}
}
Window* window_create(const char* title, int width, int height, int fullscreen) {
Window* window = (Window*)calloc(1, sizeof(Window));
if (!window) return NULL;
window->title = title;
window->width = width;
window->height = height;
window->fullscreen = fullscreen;
register_window_class();
DWORD style = WS_OVERLAPPEDWINDOW;
DWORD ex_style = WS_EX_APPWINDOW;
RECT rect = {0, 0, width, height};
AdjustWindowRectEx(&rect, style, FALSE, ex_style);
int adjusted_width = rect.right - rect.left;
int adjusted_height = rect.bottom - rect.top;
int screen_w = GetSystemMetrics(SM_CXSCREEN);
int screen_h = GetSystemMetrics(SM_CYSCREEN);
int pos_x = (screen_w - adjusted_width) / 2;
int pos_y = (screen_h - adjusted_height) / 2;
// 转换标题为宽字符
wchar_t w_title[256];
MultiByteToWideChar(CP_UTF8, 0, title, -1, w_title, 256);
window->hwnd = CreateWindowExW(
ex_style, WINDOW_CLASS, w_title, style,
pos_x, pos_y, adjusted_width, adjusted_height,
NULL, NULL, GetModuleHandleW(NULL), window
);
if (!window->hwnd) {
LOG_ERROR("Failed to create window! Error: %lu", GetLastError());
free(window);
return NULL;
}
ShowWindow(window->hwnd, SW_SHOW);
UpdateWindow(window->hwnd);
LOG_INFO("Window created: %dx%d", width, height);
return window;
}
void window_destroy(Window* window) {
if (window) {
if (window->hwnd) {
DestroyWindow(window->hwnd);
window->hwnd = NULL;
}
free(window);
}
}
void window_show(Window* window) {
if (window && window->hwnd) ShowWindow(window->hwnd, SW_SHOW);
}
void window_hide(Window* window) {
if (window && window->hwnd) ShowWindow(window->hwnd, SW_HIDE);
}
int window_process_messages(Window* window) {
MSG msg = {0};
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) return 0;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 1;
}
int window_get_width(Window* window) { return window ? window->width : 0; }
int window_get_height(Window* window) { return window ? window->height : 0; }
HWND window_get_handle(Window* window) { return window ? window->hwnd : NULL; }
int window_is_fullscreen(Window* window) { return window ? window->fullscreen : 0; }
int window_is_minimized(Window* window) { return window ? window->minimized : 0; }
void window_set_key_callback(Window* window, WindowKeyCallback callback) { g_key_callback = callback; }
void window_set_resize_callback(Window* window, WindowResizeCallback callback) { g_resize_callback = callback; }
void window_set_mouse_move_callback(Window* window, WindowMouseMoveCallback callback) { g_mouse_move_callback = callback; }
void window_set_mouse_button_callback(Window* window, WindowMouseButtonCallback callback) { g_mouse_button_callback = callback; }
+47
View File
@@ -0,0 +1,47 @@
#pragma once
// ============================================================================
// window.h - Win32 窗口管理
// ============================================================================
#include <Windows.h>
// 窗口结构
typedef struct Window {
HWND hwnd;
int width;
int height;
int fullscreen;
int minimized;
const char* title;
} Window;
// 回调类型
typedef void (*WindowKeyCallback)(int key);
typedef void (*WindowResizeCallback)(int width, int height);
typedef void (*WindowMouseMoveCallback)(int x, int y);
typedef void (*WindowMouseButtonCallback)(int button, int pressed);
// 创建/销毁
Window* window_create(const char* title, int width, int height, int fullscreen);
void window_destroy(Window* window);
// 显示/隐藏
void window_show(Window* window);
void window_hide(Window* window);
// 消息处理
int window_process_messages(Window* window);
// 获取属性
int window_get_width(Window* window);
int window_get_height(Window* window);
HWND window_get_handle(Window* window);
int window_is_fullscreen(Window* window);
int window_is_minimized(Window* window);
// 设置回调
void window_set_key_callback(Window* window, WindowKeyCallback callback);
void window_set_resize_callback(Window* window, WindowResizeCallback callback);
void window_set_mouse_move_callback(Window* window, WindowMouseMoveCallback callback);
void window_set_mouse_button_callback(Window* window, WindowMouseButtonCallback callback);
+145
View File
@@ -0,0 +1,145 @@
#pragma once
// ============================================================================
// engine_api.h - C-ASM 接口头文件
// 暴露所有汇编函数给 C 代码使用
// ============================================================================
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// 当 MASM 不可用时,使用 C fallback
// math_c_fallback.h 已经定义了所有数学类型和函数
// 不需要重复声明
// ============================================================================
// memory.asm - 对齐内存分配
// ============================================================================
#if !defined(MATH_ASM_AVAILABLE) || MATH_ASM_AVAILABLE == 0
// 在 C fallback 模式下,这些函数已在 math_c_fallback.h 中定义
// 不需要重复声明
#else
void* mem_aligned_alloc(uint64_t size, uint64_t alignment);
void mem_aligned_free(void* ptr);
void* mem_aligned_realloc(void* ptr, uint64_t size, uint64_t alignment);
// ============================================================================
// string_ops.asm - 快速字符串操作
// ============================================================================
void fast_memcpy(void* dst, const void* src, uint64_t count);
void fast_memset(void* dst, int value, uint64_t count);
uint64_t fast_strlen(const char* str);
int fast_strcmp(const char* a, const char* b);
// ============================================================================
// profiling.asm - 性能分析
// ============================================================================
uint64_t prof_rdtsc(void);
double prof_cycles_to_ms(uint64_t cycles);
uint64_t prof_get_freq(void);
// 性能计时器结构
typedef struct {
uint64_t start;
uint64_t end;
double elapsed_ms;
} ProfTimer;
void prof_timer_start(ProfTimer* timer);
void prof_timer_stop(ProfTimer* timer);
#endif
#ifdef __cplusplus
}
#endif
// ============================================================================
// C++ 便利封装
// ============================================================================
#ifdef __cplusplus
// Vec3 运算符重载
inline Vec3 operator+(const Vec3& a, const Vec3& b) {
Vec3 result;
vec3_add(&a, &b, &result);
return result;
}
inline Vec3 operator-(const Vec3& a, const Vec3& b) {
Vec3 result;
vec3_sub(&a, &b, &result);
return result;
}
inline Vec3 operator*(const Vec3& a, float s) {
Vec3 result;
vec3_scale(&a, s, &result);
return result;
}
inline float dot(const Vec3& a, const Vec3& b) {
return vec3_dot(&a, &b);
}
inline Vec3 cross(const Vec3& a, const Vec3& b) {
Vec3 result;
vec3_cross(&a, &b, &result);
return result;
}
inline float length(const Vec3& a) {
return vec3_length(&a);
}
inline Vec3 normalize(const Vec3& a) {
Vec3 result;
vec3_normalize(&a, &result);
return result;
}
inline Vec3 lerp(const Vec3& a, const Vec3& b, float t) {
Vec3 result;
vec3_lerp(&a, &b, t, &result);
return result;
}
// Mat4 运算符重载
inline Mat4 operator*(const Mat4& a, const Mat4& b) {
Mat4 result;
mat4_multiply(&a, &b, &result);
return result;
}
inline Vec3 transformPoint(const Mat4& m, const Vec3& v) {
Vec3 result;
// 使用 vec4 乘法, w=1
Vec4 v4 = {v.x, v.y, v.z, 1.0f};
Vec4 r4;
// 简化: 逐行计算
result.x = m.m[0][0]*v.x + m.m[0][1]*v.y + m.m[0][2]*v.z + m.m[0][3];
result.y = m.m[1][0]*v.x + m.m[1][1]*v.y + m.m[1][2]*v.z + m.m[1][3];
result.z = m.m[2][0]*v.x + m.m[2][1]*v.y + m.m[2][2]*v.z + m.m[2][3];
return result;
}
// Quat 运算符重载
inline Quat operator*(const Quat& a, const Quat& b) {
Quat result;
quat_multiply(&a, &b, &result);
return result;
}
inline Vec3 rotate(const Quat& q, const Vec3& v) {
Vec3 result;
quat_rotate_vec3(&q, &v, &result);
return result;
}
#endif // __cplusplus
+98
View File
@@ -0,0 +1,98 @@
#include "camera_controller.h"
#include <math.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846f
#endif
CameraController camera_controller_create(Camera* camera) {
CameraController ctrl = {0};
ctrl.camera = camera;
ctrl.mode = CAMERA_MODE_FLY;
ctrl.move_speed = 10.0f;
ctrl.mouse_sensitivity = 0.003f;
ctrl.height_offset = 1.75f;
ctrl.pitch_min = -1.45f;
ctrl.pitch_max = 1.45f;
ctrl.yaw = 0.0f;
ctrl.pitch = 0.0f;
ctrl.ground_height_fn = NULL;
ctrl.ground_user = NULL;
return ctrl;
}
void camera_controller_set_ground_callback(CameraController* ctrl,
float (*get_height)(float x, float z, void* user),
void* user) {
ctrl->ground_height_fn = get_height;
ctrl->ground_user = user;
}
void camera_controller_set_mode(CameraController* ctrl, CameraMode mode) {
ctrl->mode = mode;
}
void camera_controller_look(CameraController* ctrl, float delta_x, float delta_y) {
ctrl->yaw -= delta_x * ctrl->mouse_sensitivity;
ctrl->pitch -= delta_y * ctrl->mouse_sensitivity;
if (ctrl->pitch < ctrl->pitch_min) ctrl->pitch = ctrl->pitch_min;
if (ctrl->pitch > ctrl->pitch_max) ctrl->pitch = ctrl->pitch_max;
// 重建 target
Vec3 forward;
forward.x = cosf(ctrl->pitch) * sinf(ctrl->yaw);
forward.y = sinf(ctrl->pitch);
forward.z = cosf(ctrl->pitch) * cosf(ctrl->yaw);
vec3_normalize(&forward, &forward);
vec3_add(&ctrl->camera->position, &forward, &ctrl->camera->target);
ctrl->camera->dirty = 1;
}
void camera_controller_move(CameraController* ctrl, Vec3 dir, float dt) {
Camera* cam = ctrl->camera;
Vec3 forward, right, up;
vec3_sub(&cam->target, &cam->position, &forward);
forward.y = 0.0f;
vec3_normalize(&forward, &forward);
vec3_cross(&cam->up, &forward, &right);
vec3_normalize(&right, &right);
up = cam->up;
Vec3 move = {0};
Vec3 tmp;
vec3_scale(&right, dir.x, &tmp);
vec3_add(&move, &tmp, &move);
vec3_scale(&up, dir.y, &tmp);
vec3_add(&move, &tmp, &move);
vec3_scale(&forward, dir.z, &tmp);
vec3_add(&move, &tmp, &move);
float len = vec3_length(&move);
if (len > 0.0001f) {
vec3_scale(&move, ctrl->move_speed * dt / len, &move);
vec3_add(&cam->position, &move, &cam->position);
vec3_add(&cam->target, &move, &cam->target);
cam->dirty = 1;
}
}
void camera_controller_update(CameraController* ctrl, float dt) {
(void)dt;
if (ctrl->mode != CAMERA_MODE_WALK) return;
if (!ctrl->ground_height_fn) return;
float ground = ctrl->ground_height_fn(ctrl->camera->position.x, ctrl->camera->position.z, ctrl->ground_user);
float desired_y = ground + ctrl->height_offset;
float delta = desired_y - ctrl->camera->position.y;
if (fabsf(delta) > 0.01f) {
Vec3 move = {0, delta, 0};
vec3_add(&ctrl->camera->position, &move, &ctrl->camera->position);
vec3_add(&ctrl->camera->target, &move, &ctrl->camera->target);
ctrl->camera->dirty = 1;
}
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
// ============================================================================
// camera_controller.h - 增强相机控制器(飞行 / 步行模式)
// ============================================================================
#include "camera.h"
#include "../ASM/math_c_fallback.h"
typedef enum {
CAMERA_MODE_FLY,
CAMERA_MODE_WALK,
} CameraMode;
typedef struct {
Camera* camera;
CameraMode mode;
float move_speed;
float mouse_sensitivity;
float height_offset; // 步行模式眼睛离地面高度
float pitch_min;
float pitch_max;
float yaw;
float pitch;
float (*ground_height_fn)(float x, float z, void* user);
void* ground_user;
} CameraController;
CameraController camera_controller_create(Camera* camera);
// 更新 yaw/pitch,鼠标输入以 delta 像素为单位
void camera_controller_look(CameraController* ctrl, float delta_x, float delta_y);
// 移动:dir 为本地空间方向 (x=右, y=上, z=前)
void camera_controller_move(CameraController* ctrl, Vec3 dir, float dt);
// 设置地形高度采样回调
void camera_controller_set_ground_callback(CameraController* ctrl, float (*get_height)(float x, float z, void* user), void* user);
// 切换模式
void camera_controller_set_mode(CameraController* ctrl, CameraMode mode);
// 每帧应用约束(如步行贴地)
void camera_controller_update(CameraController* ctrl, float dt);
+60
View File
@@ -0,0 +1,60 @@
#include "frustum.h"
#include <math.h>
void frustum_from_matrix(const Mat4* vp, Vec4 planes[6]) {
// 行主序存储:clip = vec4(pos,1) * vp
// 因此 clip 分量 = dot(pos, vp 的列)
// left = col3 + col0
// right = col3 - col0
// bottom = col3 + col1
// top = col3 - col1
// near = col3 + col2
// far = col3 - col2
planes[0] = (Vec4){ vp->m[0][3] + vp->m[0][0],
vp->m[1][3] + vp->m[1][0],
vp->m[2][3] + vp->m[2][0],
vp->m[3][3] + vp->m[3][0] };
planes[1] = (Vec4){ vp->m[0][3] - vp->m[0][0],
vp->m[1][3] - vp->m[1][0],
vp->m[2][3] - vp->m[2][0],
vp->m[3][3] - vp->m[3][0] };
planes[2] = (Vec4){ vp->m[0][3] + vp->m[0][1],
vp->m[1][3] + vp->m[1][1],
vp->m[2][3] + vp->m[2][1],
vp->m[3][3] + vp->m[3][1] };
planes[3] = (Vec4){ vp->m[0][3] - vp->m[0][1],
vp->m[1][3] - vp->m[1][1],
vp->m[2][3] - vp->m[2][1],
vp->m[3][3] - vp->m[3][1] };
planes[4] = (Vec4){ vp->m[0][3] + vp->m[0][2],
vp->m[1][3] + vp->m[1][2],
vp->m[2][3] + vp->m[2][2],
vp->m[3][3] + vp->m[3][2] };
planes[5] = (Vec4){ vp->m[0][3] - vp->m[0][2],
vp->m[1][3] - vp->m[1][2],
vp->m[2][3] - vp->m[2][2],
vp->m[3][3] - vp->m[3][2] };
for (int i = 0; i < 6; i++) {
float len = sqrtf(planes[i].x * planes[i].x +
planes[i].y * planes[i].y +
planes[i].z * planes[i].z);
if (len > 0.0001f) {
planes[i].x /= len;
planes[i].y /= len;
planes[i].z /= len;
planes[i].w /= len;
}
}
}
int frustum_intersects_sphere(const Vec4 planes[6], Vec3 center, float radius) {
for (int i = 0; i < 6; i++) {
float dist = planes[i].x * center.x +
planes[i].y * center.y +
planes[i].z * center.z +
planes[i].w;
if (dist < -radius) return 0;
}
return 1;
}
Loaded 100 of 227 files, more files were not shown because too many files have changed in this diff. Show more