MiMoEngine ASM: x86-64 MASM汇编+C混合架构游戏引擎
核心热路径(数学/内存/字符串/性能分析)用MASM汇编(SSE/AVX2) 外围系统(窗口/渲染/ECS/音频/UI/脚本)用C语言 含自研编辑器、VisualScript积木编程、中文渲染(glyph_atlas)
This commit is contained in:
commit
2d56cb3bc9
91 files changed
+9988
No files matched your search
@@ -0,0 +1,127 @@
|
||||
; ============================================================================
|
||||
; 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
|
||||
+1150
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#ifndef MATH_ASM_AVAILABLE
|
||||
|
||||
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* 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
|
||||
@@ -0,0 +1,177 @@
|
||||
; ============================================================================
|
||||
; 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, 40
|
||||
|
||||
mov rbx, rdx ; rbx = new size
|
||||
mov rdi, r8 ; rdi = alignment
|
||||
|
||||
; 如果 ptr 为 NULL, 直接分配
|
||||
test rcx, rcx
|
||||
jz realloc_alloc
|
||||
|
||||
; 保存旧信息
|
||||
mov rsi, [rcx - 8] ; 原始指针
|
||||
mov r12, [rcx - 16] ; 旧 size
|
||||
|
||||
; 分配新内存
|
||||
mov rcx, rbx
|
||||
mov rdx, rdi
|
||||
call mem_aligned_alloc
|
||||
test rax, rax
|
||||
jz realloc_failed
|
||||
|
||||
; 复制旧数据 (取较小值)
|
||||
mov rdx, r12
|
||||
cmp rdx, rbx
|
||||
jbe realloc_copy_ok
|
||||
mov rdx, rbx
|
||||
|
||||
realloc_copy_ok:
|
||||
mov rdi, rax ; 保存新指针
|
||||
mov rcx, rax ; dst
|
||||
; rsi = 旧数据起始
|
||||
mov rsi, [rcx - 8] ; 旧原始指针
|
||||
; 需要获取旧的对齐偏移
|
||||
; 简化: 直接复制
|
||||
call fast_memcpy
|
||||
|
||||
; 释放旧内存
|
||||
mov rcx, rsi
|
||||
call mem_aligned_free
|
||||
|
||||
mov rax, rdi ; 返回新指针
|
||||
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, 40
|
||||
pop r12
|
||||
pop rsi
|
||||
pop rdi
|
||||
pop rbx
|
||||
ret
|
||||
mem_aligned_realloc ENDP
|
||||
|
||||
END
|
||||
@@ -0,0 +1,172 @@
|
||||
; ============================================================================
|
||||
; 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 频率 (通过 OS 或推断)
|
||||
prof_get_freq PROC
|
||||
; 检查是否已初始化
|
||||
cmp byte ptr [cpu_freq_init], 0
|
||||
jnz prof_freq_return
|
||||
|
||||
; 初始化 CPU 频率
|
||||
; 方法: 使用 QueryPerformanceCounter 测量 RDTSC
|
||||
push rbx
|
||||
push rdi
|
||||
push rsi
|
||||
sub rsp, 40
|
||||
|
||||
; 获取 QPC 频率
|
||||
lea rcx, [rsp + 32]
|
||||
call QueryPerformanceFrequency
|
||||
|
||||
; 获取起始 QPC 和 TSC
|
||||
lea rcx, [rsp + 16]
|
||||
call QueryPerformanceCounter
|
||||
rdtscp
|
||||
shl rdx, 32
|
||||
or rax, rdx
|
||||
mov rbx, rax ; rbx = 起始 TSC
|
||||
|
||||
; 等待约 10ms
|
||||
mov rcx, 10000 ; 10,000 微秒
|
||||
call Sleep
|
||||
|
||||
; 获取结束 QPC 和 TSC
|
||||
lea rcx, [rsp]
|
||||
call QueryPerformanceCounter
|
||||
rdtscp
|
||||
shl rdx, 32
|
||||
or rax, rdx
|
||||
mov rdi, rax ; rdi = 结束 TSC
|
||||
|
||||
; 计算经过的 QPC ticks
|
||||
mov rax, [rsp]
|
||||
sub rax, [rsp + 16] ; qpc_diff
|
||||
|
||||
; 计算 QPC 频率 (ticks/sec)
|
||||
mov rsi, [rsp + 32]
|
||||
|
||||
; TSC diff
|
||||
mov rax, rdi
|
||||
sub rax, rbx ; tsc_diff
|
||||
|
||||
; CPU freq = tsc_diff * qpc_freq / qpc_diff
|
||||
; 但 qpc_diff 对应 10ms, 所以要 * 100
|
||||
mov rdx, rsi
|
||||
mul rdx ; tsc_diff * qpc_freq
|
||||
; rdx:rax = result
|
||||
div qword ptr [rsp + 16] ; / qpc_diff
|
||||
; rax = freq * 100 (因为等待了 10ms)
|
||||
mov rcx, 100
|
||||
mul rcx
|
||||
|
||||
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 转换为毫秒
|
||||
prof_cycles_to_ms PROC
|
||||
; 先获取频率
|
||||
call prof_get_freq
|
||||
; rax = CPU freq (Hz)
|
||||
|
||||
; ms = cycles / (freq / 1000)
|
||||
; 或: ms = cycles * 1000 / freq
|
||||
mov rdx, 1000
|
||||
mul rdx ; freq * 1000
|
||||
; rdx:rax = freq * 1000
|
||||
|
||||
; 转换为 double
|
||||
cvtsi2sd xmm0, qword ptr [rsp - 8] ; 需要栈存储
|
||||
; 简化: 使用整数除法再转 double
|
||||
mov rcx, rax
|
||||
mov rax, [cpu_freq]
|
||||
; 重新计算
|
||||
mov rax, rcx
|
||||
; cycles * 1000 / freq
|
||||
; 已经: rax = freq * 1000, 需要 cycles * 1000 / freq
|
||||
; 调整: cycles * 1000 / freq = cycles / (freq/1000)
|
||||
|
||||
; 简化实现
|
||||
cvtsi2sd xmm0, rcx ; cycles * 1000 (转 double)
|
||||
cvtsi2sd xmm1, rax ; freq (转 double)
|
||||
divsd xmm0, xmm1 ; (cycles*1000) / freq = 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
|
||||
@@ -0,0 +1,325 @@
|
||||
; ============================================================================
|
||||
; 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 rax, rcx ; rax = str
|
||||
xor rcx, rcx ; rcx = 计数器
|
||||
|
||||
; 每次检查 16 字节
|
||||
pxor xmm0, xmm0 ; 搜索 0
|
||||
|
||||
strlen_sse_loop:
|
||||
; 检查是否接近字符串末尾 (避免越界读取)
|
||||
; 简化: 逐 16 字节检查
|
||||
movdqu xmm1, xmmword ptr [rax + rcx]
|
||||
pcmpestrm xmm0, xmm1, 00001000b ; 搜索 0, 返回掩码
|
||||
jc strlen_found
|
||||
|
||||
add rcx, 16
|
||||
jmp strlen_sse_loop
|
||||
|
||||
strlen_found:
|
||||
; 获取第一个匹配位置
|
||||
pcmpestri xmm0, xmm1, 00001000b ; ECX = 匹配位置
|
||||
add rax, rcx
|
||||
add rax, rcx
|
||||
; 需要计算实际位置
|
||||
; 简化: 使用标量回退
|
||||
mov rax, rcx
|
||||
ret
|
||||
|
||||
; 标量回退
|
||||
strlen_scalar:
|
||||
mov rax, rcx
|
||||
strlen_scalar_loop:
|
||||
cmp byte ptr [rax], 0
|
||||
je strlen_scalar_done
|
||||
inc rax
|
||||
jmp strlen_scalar_loop
|
||||
strlen_scalar_done:
|
||||
sub rax, rcx
|
||||
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
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "audio.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
AudioSystem* audio_create(void) {
|
||||
AudioSystem* audio = (AudioSystem*)calloc(1, sizeof(AudioSystem));
|
||||
return audio;
|
||||
}
|
||||
|
||||
void audio_destroy(AudioSystem* audio) {
|
||||
free(audio);
|
||||
}
|
||||
|
||||
AudioSource* audio_source_create(AudioSystem* audio) {
|
||||
AudioSource* source = (AudioSource*)calloc(1, sizeof(AudioSource));
|
||||
source->volume = 1.0f;
|
||||
source->pitch = 1.0f;
|
||||
return source;
|
||||
}
|
||||
|
||||
void audio_source_destroy(AudioSource* source) {
|
||||
free(source);
|
||||
}
|
||||
|
||||
void audio_source_play(AudioSource* source) { (void)source; }
|
||||
void audio_source_stop(AudioSource* source) { (void)source; }
|
||||
void audio_source_pause(AudioSource* source) { (void)source; }
|
||||
void audio_source_resume(AudioSource* source) { (void)source; }
|
||||
void audio_source_set_volume(AudioSource* source, float v) { if (source) source->volume = v; }
|
||||
void audio_source_set_pitch(AudioSource* source, float p) { if (source) source->pitch = p; }
|
||||
void audio_source_set_loop(AudioSource* source, int l) { if (source) source->loop = l; }
|
||||
int audio_load_wav(AudioSource* source, const char* path) { (void)source; (void)path; return 0; }
|
||||
int audio_load_wav_memory(AudioSource* source, const uint8_t* data, uint32_t size) { (void)source; (void)data; (void)size; return 0; }
|
||||
void audio_set_master_volume(AudioSystem* audio, float v) { (void)audio; (void)v; }
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// audio.h - 音频系统 (XAudio2)
|
||||
// ============================================================================
|
||||
|
||||
#include <xaudio2.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// 音频源
|
||||
typedef struct {
|
||||
IXAudio2SourceVoice* source;
|
||||
IXAudio2Voice* mastering;
|
||||
float volume;
|
||||
float pitch;
|
||||
int loop;
|
||||
} AudioSource;
|
||||
|
||||
// 音频管理器
|
||||
typedef struct {
|
||||
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);
|
||||
@@ -0,0 +1,157 @@
|
||||
// ============================================================================
|
||||
// engine.c - 引擎核心实现
|
||||
// ============================================================================
|
||||
|
||||
#include "engine.h"
|
||||
#include "logger.h"
|
||||
#include "timer.h"
|
||||
#include "../Window/window.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 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);
|
||||
}
|
||||
}
|
||||
|
||||
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 MiMo Engine ASM...");
|
||||
|
||||
// 初始化计时器
|
||||
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);
|
||||
|
||||
LOG_INFO("Window created: %dx%d", config->window_width, config->window_height);
|
||||
|
||||
engine->initialized = 1;
|
||||
engine->running = 1;
|
||||
engine->frame_count = 0;
|
||||
|
||||
LOG_INFO("MiMo Engine ASM initialized successfully.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
void engine_run(Engine* engine) {
|
||||
if (!engine->initialized) {
|
||||
LOG_ERROR("Engine not initialized!");
|
||||
return;
|
||||
}
|
||||
|
||||
while (engine->running) {
|
||||
// 处理窗口消息
|
||||
if (!window_process_messages(engine->window)) {
|
||||
engine->running = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// 更新计时
|
||||
timer_tick();
|
||||
engine->delta_time = timer_get_delta();
|
||||
engine->total_time = timer_get_total();
|
||||
engine->frame_count++;
|
||||
|
||||
// 用户更新
|
||||
if (g_update_callback) {
|
||||
g_update_callback(engine, engine->delta_time);
|
||||
}
|
||||
|
||||
// 用户渲染
|
||||
if (g_render_callback) {
|
||||
g_render_callback(engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void engine_shutdown(Engine* engine) {
|
||||
if (!engine->initialized) return;
|
||||
|
||||
LOG_INFO("Shutting down MiMo Engine ASM...");
|
||||
|
||||
if (engine->renderer) {
|
||||
// renderer_destroy(engine->renderer);
|
||||
engine->renderer = 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;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// engine.h - 引擎核心
|
||||
// ============================================================================
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// 前向声明
|
||||
typedef struct Window Window;
|
||||
|
||||
// 引擎配置
|
||||
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;
|
||||
double delta_time;
|
||||
double total_time;
|
||||
uint64_t frame_count;
|
||||
} Engine;
|
||||
|
||||
// 初始化默认配置
|
||||
static inline EngineConfig engine_default_config(void) {
|
||||
EngineConfig config;
|
||||
config.app_name = "MiMo Engine ASM";
|
||||
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);
|
||||
|
||||
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);
|
||||
@@ -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, "=== MiMo Engine ASM Log ===\n");
|
||||
fflush(g_log_file);
|
||||
}
|
||||
}
|
||||
printf("=== MiMo Engine ASM 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);
|
||||
}
|
||||
}
|
||||
@@ -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__)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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, "MiMo 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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>
|
||||
@@ -0,0 +1,137 @@
|
||||
#include "input.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -0,0 +1,8 @@
|
||||
// math_impl.c - 数学函数实现 (C fallback)
|
||||
// 当 MASM 不可用时使用
|
||||
|
||||
#include "math_c_fallback.h"
|
||||
|
||||
// 由于函数是 static inline,它们应该被内联
|
||||
// 但如果编译器没有内联,这些函数将提供实现
|
||||
// 这个文件确保链接器能找到符号
|
||||
@@ -0,0 +1,202 @@
|
||||
#include "physics.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
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_static = 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) {
|
||||
Collider* c = &world->colliders[index];
|
||||
if (c->type == COLLIDER_SPHERE) {
|
||||
c->sphere.center = position;
|
||||
}
|
||||
}
|
||||
|
||||
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(¢erA, ¢erB, &diff);
|
||||
Vec3 half_a, half_b, size;
|
||||
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, diff.x), 0, 0};
|
||||
else if (dy > dz) { normal->x = 0; normal->y = copysignf(1, diff.y); normal->z = 0; }
|
||||
else { normal->x = 0; normal->y = 0; normal->z = copysignf(1, 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;
|
||||
}
|
||||
|
||||
void physics_step(PhysicsWorld* world, float dt) {
|
||||
(void)world;
|
||||
(void)dt;
|
||||
}
|
||||
|
||||
void physics_set_gravity(PhysicsWorld* world, Vec3 gravity) {
|
||||
world->gravity = gravity;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#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 {
|
||||
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;
|
||||
} Collider;
|
||||
|
||||
typedef struct {
|
||||
Vec3 velocity;
|
||||
Vec3 acceleration;
|
||||
float mass;
|
||||
float restitution;
|
||||
float friction;
|
||||
int is_kinematic;
|
||||
} RigidBody;
|
||||
|
||||
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);
|
||||
|
||||
// 模拟
|
||||
void physics_step(PhysicsWorld* world, float dt);
|
||||
void physics_set_gravity(PhysicsWorld* world, Vec3 gravity);
|
||||
@@ -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(&forward, &camera->up, &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;
|
||||
}
|
||||
@@ -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 "graphics_device.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.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);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <d3d11.h>
|
||||
#include <dxgi.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
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;
|
||||
|
||||
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);
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "mesh.h"
|
||||
#include <stdlib.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);
|
||||
if (m->ib) {
|
||||
ctx->lpVtbl->IASetIndexBuffer(ctx, m->ib, DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->lpVtbl->DrawIndexed(ctx, m->icount, 0, 0);
|
||||
} else {
|
||||
ctx->lpVtbl->Draw(ctx, m->vcount, 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;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#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);
|
||||
void mesh_destroy(Mesh* m);
|
||||
void mesh_draw(Mesh* m, ID3D11DeviceContext* ctx);
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "renderer.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
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};
|
||||
return r;
|
||||
}
|
||||
|
||||
void renderer_destroy(Renderer* r) { free(r); }
|
||||
|
||||
void renderer_begin_frame(Renderer* r) {
|
||||
graphics_device_clear(r->device, 0.1f, 0.1f, 0.15f, 1.0f);
|
||||
}
|
||||
|
||||
void renderer_end_frame(Renderer* r) {
|
||||
graphics_device_present(r->device);
|
||||
}
|
||||
|
||||
void renderer_draw_mesh(Renderer* r, Mesh* m) {
|
||||
if (!m) return;
|
||||
ID3D11DeviceContext* ctx = r->device->context;
|
||||
UINT stride = sizeof(Vertex);
|
||||
UINT offset = 0;
|
||||
ctx->lpVtbl->IASetVertexBuffers(ctx, 0, 1, &m->vb, &stride, &offset);
|
||||
if (m->ib) {
|
||||
ctx->lpVtbl->IASetIndexBuffer(ctx, m->ib, DXGI_FORMAT_R32_UINT, 0);
|
||||
ctx->lpVtbl->DrawIndexed(ctx, m->icount, 0, 0);
|
||||
} else {
|
||||
ctx->lpVtbl->Draw(ctx, m->vcount, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void renderer_draw_mesh_with_shader(Renderer* r, Mesh* m, Shader* s, const Mat4* world, Texture* t) {
|
||||
if (!m || !s) return;
|
||||
ID3D11DeviceContext* ctx = r->device->context;
|
||||
shader_bind(s, ctx);
|
||||
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; }
|
||||
GraphicsDevice* renderer_get_device(Renderer* r) { return r ? r->device : NULL; }
|
||||
|
||||
void renderer_resize(Renderer* r, int width, int height) {
|
||||
if (r && r->device && width > 0 && height > 0) {
|
||||
graphics_device_resize(r->device, width, height);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "graphics_device.h"
|
||||
#include "shader.h"
|
||||
#include "mesh.h"
|
||||
#include "texture.h"
|
||||
#include "camera.h"
|
||||
#include "../include/engine_api.h"
|
||||
#include "../ASM/math_c_fallback.h"
|
||||
|
||||
typedef struct {
|
||||
GraphicsDevice* device;
|
||||
Shader* current_shader;
|
||||
Mat4 view;
|
||||
Mat4 projection;
|
||||
Vec3 light_dir;
|
||||
Vec4 light_color;
|
||||
Vec4 ambient_color;
|
||||
} Renderer;
|
||||
|
||||
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_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);
|
||||
GraphicsDevice* renderer_get_device(Renderer* renderer);
|
||||
void renderer_resize(Renderer* renderer, int width, int height);
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "shader.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;float4 col:COLOR;};"
|
||||
"cbuffer CB:register(b0){float4x4 WVP;};"
|
||||
"PS_IN main(VS_IN i){PS_IN o;o.pos=mul(float4(i.pos,1),WVP);o.col=i.col;return o;}";
|
||||
|
||||
static const char* default_ps =
|
||||
"struct PS_IN{float4 pos:SV_POSITION;float4 col:COLOR;};"
|
||||
"float4 main(PS_IN i):SV_TARGET{return i.col;}";
|
||||
|
||||
static ID3DBlob* compile_shader(const char* code, uint32_t len, const char* target) {
|
||||
ID3DBlob *blob = NULL, *err = NULL;
|
||||
D3DCompile(code, len, NULL, NULL, NULL, "main", target, D3DCOMPILE_OPTIMIZATION_LEVEL3, 0, &blob, &err);
|
||||
if (err) 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) {
|
||||
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);
|
||||
}
|
||||
|
||||
void shader_set_matrix(Shader* s, ID3D11DeviceContext* ctx, const float* data) {
|
||||
ctx->lpVtbl->UpdateSubresource(ctx, (ID3D11Resource*)s->cb, 0, NULL, data, 0, 0);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -0,0 +1,188 @@
|
||||
#include "texture.h"
|
||||
|
||||
#include <wincodec.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#pragma comment(lib, "windowscodecs.lib")
|
||||
|
||||
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;
|
||||
ID3D11DeviceContext_PSSetShaderResources(ctx, slot, 1, &texture->srv);
|
||||
ID3D11DeviceContext_PSSetSamplers(ctx, slot, 1, &(ID3D11SamplerState*){NULL});
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
#include "ui.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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; };
|
||||
float4 main(PS_INPUT input) : SV_TARGET { return input.Color; }
|
||||
)";
|
||||
|
||||
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);
|
||||
|
||||
return ui;
|
||||
}
|
||||
|
||||
void ui_destroy(UI* ui) {
|
||||
if (!ui) return;
|
||||
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;
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x, y, 0, 0, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y, 1, 0, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x + w, y + h, 1, 1, color};
|
||||
ui->vertices[ui->vertex_count++] = (UIVertex){x, y + h, 0, 1, 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_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) {
|
||||
(void)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);
|
||||
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) {
|
||||
(void)text;
|
||||
float s = 16.0f;
|
||||
ui_draw_rect_border(ui, x, y, s, s, checked ? 0xFF0078D4 : 0xFF333333, 0xFF666666, 1);
|
||||
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) {
|
||||
(void)text;
|
||||
ui_draw_rect(ui, x, y, (float)(strlen(text) * 8), ui->font_size, 0x00000000);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#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;
|
||||
|
||||
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;
|
||||
} 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);
|
||||
|
||||
// 控件
|
||||
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);
|
||||
@@ -0,0 +1,135 @@
|
||||
// ============================================================================
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 销毁积木
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
#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);
|
||||
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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -0,0 +1,191 @@
|
||||
// ============================================================================
|
||||
// block_executor.c - 积木执行引擎实现
|
||||
// ============================================================================
|
||||
|
||||
#include "block_executor.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
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->current_block = NULL;
|
||||
|
||||
// 找到 on_start 事件积木
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void executor_step(BlockExecutor* exec) {
|
||||
if (!exec->current_block) return;
|
||||
|
||||
// 执行当前积木
|
||||
Block* current = exec->current_block;
|
||||
Block* next = NULL;
|
||||
|
||||
switch (current->type) {
|
||||
case BLOCK_EVENT_START:
|
||||
case BLOCK_EVENT_UPDATE:
|
||||
// 执行语句输出连接的积木
|
||||
if (current->output_count > 0) {
|
||||
BlockSlot* out = ¤t->outputs[0];
|
||||
if (out->connected && out->connected->owner) {
|
||||
next = out->connected->owner;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCK_ACTION_MOVE:
|
||||
// 获取参数
|
||||
if (current->input_count >= 4) {
|
||||
SlotValue x = block_slot_get_value(¤t->inputs[1]);
|
||||
SlotValue y = block_slot_get_value(¤t->inputs[2]);
|
||||
SlotValue z = block_slot_get_value(¤t->inputs[3]);
|
||||
// TODO: 实际移动物体
|
||||
}
|
||||
next = current->next;
|
||||
break;
|
||||
|
||||
case BLOCK_CONTROL_IF:
|
||||
if (current->input_count >= 2) {
|
||||
SlotValue cond = block_slot_get_value(¤t->inputs[1]);
|
||||
if (cond.as.boolean) {
|
||||
// 执行 Then 分支
|
||||
if (current->output_count > 0) {
|
||||
BlockSlot* out = ¤t->outputs[0];
|
||||
if (out->connected && out->connected->owner) {
|
||||
next = out->connected->owner;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 执行 Else 分支
|
||||
if (current->output_count > 1) {
|
||||
BlockSlot* out = ¤t->outputs[1];
|
||||
if (out->connected && out->connected->owner) {
|
||||
next = out->connected->owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCK_OP_ADD:
|
||||
if (current->input_count >= 2) {
|
||||
SlotValue a = block_slot_get_value(¤t->inputs[0]);
|
||||
SlotValue b = block_slot_get_value(¤t->inputs[1]);
|
||||
SlotValue result;
|
||||
result.type = SLOT_VALUE_NUMBER;
|
||||
result.as.number = a.as.number + b.as.number;
|
||||
if (current->output_count > 0) {
|
||||
current->outputs[0].value = result;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCK_VAR_SET:
|
||||
if (current->input_count >= 2) {
|
||||
SlotValue val = block_slot_get_value(¤t->inputs[1]);
|
||||
// TODO: 获取变量名
|
||||
}
|
||||
next = current->next;
|
||||
break;
|
||||
|
||||
default:
|
||||
next = current->next;
|
||||
break;
|
||||
}
|
||||
|
||||
exec->current_block = next;
|
||||
|
||||
if (!exec->current_block) {
|
||||
exec->state = EXEC_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
void executor_update(BlockExecutor* exec, double delta_time) {
|
||||
if (exec->state != EXEC_RUNNING) return;
|
||||
|
||||
// 处理等待
|
||||
if (exec->state == EXEC_WAITING) {
|
||||
exec->wait_timer -= delta_time;
|
||||
if (exec->wait_timer <= 0) {
|
||||
exec->state = EXEC_RUNNING;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行多步以避免卡顿
|
||||
int steps = 0;
|
||||
while (exec->current_block && steps < 100) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
SlotValue empty = {0};
|
||||
return empty;
|
||||
}
|
||||
|
||||
void executor_create_variable(BlockExecutor* exec, const char* name) {
|
||||
SlotValue val = {0};
|
||||
val.type = SLOT_VALUE_NUMBER;
|
||||
val.as.number = 0;
|
||||
executor_set_variable(exec, name, val);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
// ============================================================================
|
||||
// block_executor.h - 积木执行引擎
|
||||
// ============================================================================
|
||||
|
||||
#include "block_workspace.h"
|
||||
|
||||
#define MAX_VARIABLES 256
|
||||
#define MAX_CALL_STACK 32
|
||||
#define MAX_EXEC_STACK 1024
|
||||
|
||||
// 变量
|
||||
typedef struct {
|
||||
char name[64];
|
||||
SlotValue value;
|
||||
int used;
|
||||
} Variable;
|
||||
|
||||
// 调用帧
|
||||
typedef struct {
|
||||
Block* block;
|
||||
Block* return_block;
|
||||
} CallFrame;
|
||||
|
||||
// 执行状态
|
||||
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;
|
||||
|
||||
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);
|
||||
@@ -0,0 +1,137 @@
|
||||
// ============================================================================
|
||||
// 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)
|
||||
if (key == 'C' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
|
||||
// TODO: 复制到剪贴板
|
||||
}
|
||||
if (key == 'V' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
|
||||
// TODO: 从剪贴板粘贴
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -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);
|
||||
@@ -0,0 +1,142 @@
|
||||
#include "block_renderer.h"
|
||||
#include "block_core.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
float nx = -dy / len;
|
||||
float ny = dx / len;
|
||||
|
||||
block_renderer_add_quad(renderer, x1, y1, x2 - x1, y2 - y1, r, g, b, a);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
(void)renderer; (void)x; (void)y; (void)r; (void)g; (void)b;
|
||||
}
|
||||
@@ -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,142 @@
|
||||
// ============================================================================
|
||||
// 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;
|
||||
}
|
||||
|
||||
static void json_free(JsonBuffer* buf) {
|
||||
free(buf->buffer);
|
||||
buf->buffer = NULL;
|
||||
buf->size = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int serializer_load_from_string(BlockWorkspace* ws, const char* json_str) {
|
||||
// 简化的 JSON 解析
|
||||
// 完整实现需要完整的 JSON 解析器
|
||||
|
||||
workspace_clear(ws);
|
||||
|
||||
// 查找 "blocks" 数组
|
||||
const char* blocks_start = strstr(json_str, "\"blocks\"");
|
||||
if (!blocks_start) return 0;
|
||||
|
||||
// 简化解析 - 只提取基本信息
|
||||
// 实际项目中应使用 cJSON 或类似库
|
||||
|
||||
// TODO: 实现完整的 JSON 解析
|
||||
|
||||
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);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,172 @@
|
||||
// ============================================================================
|
||||
// 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;
|
||||
|
||||
block->id = ws->next_block_id++;
|
||||
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);
|
||||
@@ -0,0 +1,192 @@
|
||||
// ============================================================================
|
||||
// window.c - Win32 窗口实现
|
||||
// ============================================================================
|
||||
|
||||
#include "window.h"
|
||||
#include "../Core/logger.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
// 窗口类名
|
||||
static const wchar_t* WINDOW_CLASS = L"MiMoEngineWindow";
|
||||
|
||||
// 全局回调
|
||||
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:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(0);
|
||||
return 0;
|
||||
case WM_RBUTTONDOWN:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(1);
|
||||
return 0;
|
||||
case WM_MBUTTONDOWN:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(2);
|
||||
return 0;
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(0);
|
||||
return 0;
|
||||
case WM_RBUTTONUP:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(1);
|
||||
return 0;
|
||||
case WM_MBUTTONUP:
|
||||
if (g_mouse_button_callback) g_mouse_button_callback(2);
|
||||
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; }
|
||||
@@ -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);
|
||||
|
||||
// 创建/销毁
|
||||
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);
|
||||
@@ -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 - 对齐内存分配
|
||||
// ============================================================================
|
||||
|
||||
#ifndef MATH_ASM_AVAILABLE
|
||||
// 在 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
|
||||
Reference in new issue
Block a user