feat: major engine overhaul - scene demo with lighting, sphere, ground

- Scene Demo: cube + sphere + ground plane with proper lighting
- Audio: XAudio2 dynamic loading + WAV playback
- Physics: expanded collision system
- Renderer: improved pipeline with materials
- UI: extended widget system
- VisualScript: enhanced serializer + block interactions
- ASM: optimized math/memory/profiling routines
- Window: mouse input + scroll support
This commit is contained in:
shuiyu1123 committed 2026-07-12 08:21:07 +08:00
1 parent 2653926d85
commit 0669d102ce
35 files changed
+1844 -314

No files matched your search

+24
View File
@@ -33,6 +33,30 @@ Block* block_create(BlockType type, float x, float y) {
return block;
}
// 复制积木 (深拷贝,不复制连接关系)
Block* block_copy(const Block* src) {
if (!src) return NULL;
Block* block = (Block*)malloc(sizeof(Block));
if (!block) return NULL;
memcpy(block, src, sizeof(Block));
// 重新设置拥有者,断开所有连接
for (int i = 0; i < MAX_BLOCK_INPUTS; i++) {
block->inputs[i].owner = block;
block->inputs[i].connected = NULL;
}
for (int i = 0; i < MAX_BLOCK_OUTPUTS; i++) {
block->outputs[i].owner = block;
block->outputs[i].connected = NULL;
}
block->next = NULL;
block->parent_if = NULL;
block->workspace = NULL;
block->user_data = NULL;
return block;
}
// 销毁积木
void block_destroy(Block* block) {
if (!block) return;
+1
View File
@@ -171,6 +171,7 @@ typedef struct BlockConnection {
// 函数声明
Block* block_create(BlockType type, float x, float y);
Block* block_copy(const Block* src);
void block_destroy(Block* block);
BlockSlot* block_add_input(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
BlockSlot* block_add_output(Block* block, SlotKind kind, SlotValueType value_type, const char* label);
+90 -16
View File
@@ -51,6 +51,7 @@ void executor_destroy(BlockExecutor* exec) { free(exec); }
void executor_start(BlockExecutor* exec) {
exec->state = EXEC_RUNNING;
exec->call_stack_top = 0;
exec->loop_stack_top = 0;
exec->current_block = NULL;
for (int i = 0; i < exec->workspace->block_count; i++) {
Block* block = exec->workspace->blocks[i];
@@ -76,6 +77,26 @@ static Block* next_from_output(Block* block, int output_index) {
return NULL;
}
// 查找循环体链的最后一个积木(用于确定循环结束后应跳转到的积木)
static Block* find_loop_body_end(Block* loop_block) {
if (!loop_block || loop_block->output_count == 0 || !loop_block->outputs[0].connected) return NULL;
Block* cur = loop_block->outputs[0].connected->owner;
Block* prev = loop_block;
int guard = 0;
while (cur && guard < 1000) {
guard++;
if (cur == loop_block) return prev; // 回到循环头,则前一个为体尾
Block* next = NULL;
if (cur->output_count > 0 && cur->outputs[0].connected) {
next = cur->outputs[0].connected->owner;
}
if (!next) return cur;
prev = cur;
cur = next;
}
return cur;
}
// 获取值输入
static SlotValue get_input(Block* block, int idx) {
if (idx >= block->input_count) return make_number(0);
@@ -174,22 +195,54 @@ void executor_step(BlockExecutor* exec) {
next = next_from_output(cur, cond ? 0 : 1);
break;
}
case BLOCK_CONTROL_REPEAT: {
case BLOCK_CONTROL_REPEAT:
case BLOCK_CONTROL_WHILE: {
int cond = get_input(cur, 1).as.boolean;
if (cond) next = next_from_output(cur, 0);
else next = next_from_output(cur, 0); // 退出循环: 走输出
LoopFrame* frame = NULL;
if (exec->loop_stack_top > 0 && exec->loop_stack[exec->loop_stack_top - 1].loop_block == cur) {
frame = &exec->loop_stack[exec->loop_stack_top - 1];
} else {
if (exec->loop_stack_top < MAX_LOOP_STACK) {
frame = &exec->loop_stack[exec->loop_stack_top++];
frame->loop_block = cur;
frame->counter = 0;
frame->target = 0;
Block* body_end = find_loop_body_end(cur);
frame->after_block = body_end ? next_from_output(body_end, 0) : NULL;
}
}
if (frame && cond) {
frame->counter++;
next = next_from_output(cur, 0);
} else {
if (frame) exec->loop_stack_top--;
next = frame ? frame->after_block : next_from_output(cur, 0);
}
break;
}
case BLOCK_CONTROL_REPEAT_TIMES: {
double times = get_input(cur, 1).as.number;
// 简化: 只执行一次,实际需要计数器
log_info("循环 %.0f 次 (简化: 执行一次)", times);
next = next_from_output(cur, 0);
break;
}
case BLOCK_CONTROL_WHILE: {
int cond = get_input(cur, 1).as.boolean;
if (cond) next = next_from_output(cur, 0);
LoopFrame* frame = NULL;
if (exec->loop_stack_top > 0 && exec->loop_stack[exec->loop_stack_top - 1].loop_block == cur) {
frame = &exec->loop_stack[exec->loop_stack_top - 1];
frame->counter++;
} else {
if (exec->loop_stack_top < MAX_LOOP_STACK) {
frame = &exec->loop_stack[exec->loop_stack_top++];
frame->loop_block = cur;
frame->counter = 0;
frame->target = (int)times;
Block* body_end = find_loop_body_end(cur);
frame->after_block = body_end ? next_from_output(body_end, 0) : NULL;
}
}
if (frame && frame->counter < frame->target) {
log_info("循环 %d / %d", frame->counter + 1, frame->target);
next = next_from_output(cur, 0);
} else {
if (frame) exec->loop_stack_top--;
next = frame ? frame->after_block : next_from_output(cur, 0);
}
break;
}
case BLOCK_CONTROL_WAIT: {
@@ -320,19 +373,32 @@ void executor_step(BlockExecutor* exec) {
case BLOCK_FUNC_CALL: {
const char* fname = get_input(cur, 1).as.string;
log_info("调用函数 '%s'", fname);
if (exec->call_stack_top < MAX_CALL_STACK) {
// 在工作区中查找同名函数定义积木
Block* def = NULL;
for (int i = 0; i < exec->workspace->block_count; i++) {
Block* b = exec->workspace->blocks[i];
if (b->type == BLOCK_CONTROL_FUNCTION_DEF && strcmp(b->label, fname) == 0) {
def = b;
break;
}
}
if (def && exec->call_stack_top < MAX_CALL_STACK) {
exec->call_stack[exec->call_stack_top].block = cur;
exec->call_stack[exec->call_stack_top].return_block = next_from_output(cur, 0);
exec->call_stack_top++;
next = next_from_output(def, 0);
} else {
if (!def) log_error("未找到函数定义: '%s'", fname);
next = next_from_output(cur, 0);
}
// TODO: 查找函数定义积木并跳转
next = next_from_output(cur, 0);
break;
}
case BLOCK_FUNC_RETURN: {
if (exec->call_stack_top > 0) {
exec->call_stack_top--;
Block* caller = exec->call_stack[exec->call_stack_top].block;
next = next_from_output(caller, 0);
next = exec->call_stack[exec->call_stack_top].return_block;
}
break;
}
@@ -343,6 +409,14 @@ void executor_step(BlockExecutor* exec) {
break;
}
// 若当前 next 是某个活跃循环的 after_block,则跳转回循环头继续判断
if (next && exec->loop_stack_top > 0) {
LoopFrame* frame = &exec->loop_stack[exec->loop_stack_top - 1];
if (next == frame->after_block) {
next = frame->loop_block;
}
}
exec->current_block = next;
if (!exec->current_block) {
log_info("执行完成");
+12
View File
@@ -9,6 +9,7 @@
#define MAX_VARIABLES 256
#define MAX_CALL_STACK 32
#define MAX_EXEC_STACK 1024
#define MAX_LOOP_STACK 32
// 变量
typedef struct {
@@ -23,6 +24,14 @@ typedef struct {
Block* return_block;
} CallFrame;
// 循环帧
typedef struct {
Block* loop_block;
int counter;
int target;
Block* after_block;
} LoopFrame;
// 执行状态
typedef enum {
EXEC_IDLE = 0,
@@ -43,6 +52,9 @@ typedef struct BlockExecutor {
CallFrame call_stack[MAX_CALL_STACK];
int call_stack_top;
LoopFrame loop_stack[MAX_LOOP_STACK];
int loop_stack_top;
Block* current_block;
double wait_timer;
+16 -2
View File
@@ -116,11 +116,25 @@ void interaction_key_down(BlockInteraction* inter, BlockWorkspace* ws, int key)
}
// 复制/粘贴 (Ctrl+C / Ctrl+V)
static Block* clipboard = NULL;
if (key == 'C' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
// TODO: 复制到剪贴板
if (inter->selected_block) {
if (clipboard) block_destroy(clipboard);
clipboard = block_copy(inter->selected_block);
}
}
if (key == 'V' && (GetAsyncKeyState(VK_CONTROL) & 0x8000)) {
// TODO: 从剪贴板粘贴
if (clipboard) {
Block* copy = block_copy(clipboard);
if (copy) {
copy->x += 20.0f;
copy->y += 20.0f;
copy->id = ws->next_block_id++;
workspace_add_block(ws, copy);
inter->selected_block = copy;
ws->selected_block = copy;
}
}
}
}
+52 -4
View File
@@ -2,6 +2,10 @@
#include "block_core.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
// 5x7 位图字体 (列优先, LSB 在上)
#include "block_font.inc"
int block_renderer_init(BlockRenderer* renderer, void* device, void* context) {
memset(renderer, 0, sizeof(BlockRenderer));
@@ -94,10 +98,30 @@ void block_renderer_add_line(BlockRenderer* renderer, float x1, float y1, float
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);
const float thickness = 1.0f;
float nx = -dy / len * thickness * 0.5f;
float ny = dx / len * thickness * 0.5f;
if (renderer->vertex_count + 4 > renderer->vertex_capacity) return;
if (renderer->index_count + 6 > renderer->index_capacity) return;
uint32_t base = renderer->vertex_count;
float* v = renderer->vertex_data + base * 7;
v[0*7+0] = x1 + nx; v[0*7+1] = y1 + ny; v[0*7+2] = 0; v[0*7+3] = r; v[0*7+4] = g; v[0*7+5] = b; v[0*7+6] = a;
v[1*7+0] = x2 + nx; v[1*7+1] = y2 + ny; v[1*7+2] = 0; v[1*7+3] = r; v[1*7+4] = g; v[1*7+5] = b; v[1*7+6] = a;
v[2*7+0] = x2 - nx; v[2*7+1] = y2 - ny; v[2*7+2] = 0; v[2*7+3] = r; v[2*7+4] = g; v[2*7+5] = b; v[2*7+6] = a;
v[3*7+0] = x1 - nx; v[3*7+1] = y1 - ny; v[3*7+2] = 0; v[3*7+3] = r; v[3*7+4] = g; v[3*7+5] = b; v[3*7+6] = a;
renderer->vertex_count += 4;
renderer->index_data[renderer->index_count++] = base + 0;
renderer->index_data[renderer->index_count++] = base + 1;
renderer->index_data[renderer->index_count++] = base + 2;
renderer->index_data[renderer->index_count++] = base + 0;
renderer->index_data[renderer->index_count++] = base + 2;
renderer->index_data[renderer->index_count++] = base + 3;
}
static void block_renderer_add_bezier(BlockRenderer* renderer, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float r, float g, float b, float a) {
@@ -122,6 +146,10 @@ void block_renderer_draw_block(BlockRenderer* renderer, Block* block, float view
block_renderer_add_quad(renderer, x, y, w, h, 0.3f, 0.3f, 0.35f, 1.0f);
block_renderer_add_quad(renderer, x, y, w, 20 * zoom, 0.2f, 0.2f, 0.25f, 1.0f);
if (block->label[0]) {
block_renderer_draw_text(renderer, block->label, x + 4.0f * zoom, y + 4.0f * zoom, 0.9f, 0.9f, 0.9f);
}
}
void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, BlockSlot* to, float view_x, float view_y, float zoom) {
@@ -138,5 +166,25 @@ void block_renderer_draw_connection(BlockRenderer* renderer, BlockSlot* from, Bl
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;
const float scale = 2.0f;
const float advance = 5.0f * scale + 1.0f;
float cx = x;
for (const char* p = text; *p; ++p) {
unsigned char ch = (unsigned char)*p;
if (ch < 32 || ch > 127) ch = '?';
int idx = ch - 32;
for (int col = 0; col < 5; ++col) {
uint8_t column = BLOCK_FONT[idx][col];
for (int row = 0; row < 7; ++row) {
if (column & (1u << row)) {
block_renderer_add_quad(renderer,
cx + col * scale,
y + row * scale,
scale, scale,
r, g, b, 1.0f);
}
}
}
cx += advance;
}
}
+294 -21
View File
@@ -32,12 +32,6 @@ static void json_append(JsonBuffer* buf, const char* str) {
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;
@@ -98,9 +92,38 @@ char* serializer_save_to_string(BlockWorkspace* ws) {
snprintf(tmp, 256, " \"x\": %.1f,\n", block->x);
json_append(&buf, tmp);
snprintf(tmp, 256, " \"y\": %.1f\n", block->y);
snprintf(tmp, 256, " \"y\": %.1f,\n", block->y);
json_append(&buf, tmp);
snprintf(tmp, 256, " \"label\": \"%s\"", block->label);
json_append(&buf, tmp);
// 保存输入槽默认值
if (block->input_count > 0) {
json_append(&buf, ",\n \"inputs\": [\n");
for (int j = 0; j < block->input_count; j++) {
BlockSlot* slot = &block->inputs[j];
SlotValue* val = &slot->value;
switch (val->type) {
case SLOT_VALUE_NUMBER:
snprintf(tmp, 256, " {\"type\": \"number\", \"value\": %.6g}%s\n", val->as.number, j < block->input_count - 1 ? "," : "");
break;
case SLOT_VALUE_STRING:
snprintf(tmp, 256, " {\"type\": \"string\", \"value\": \"%s\"}%s\n", val->as.string, j < block->input_count - 1 ? "," : "");
break;
case SLOT_VALUE_BOOLEAN:
snprintf(tmp, 256, " {\"type\": \"boolean\", \"value\": %s}%s\n", val->as.boolean ? "true" : "false", j < block->input_count - 1 ? "," : "");
break;
default:
snprintf(tmp, 256, " {\"type\": \"number\", \"value\": 0}%s\n", j < block->input_count - 1 ? "," : "");
break;
}
json_append(&buf, tmp);
}
json_append(&buf, " ]");
}
json_append(&buf, "\n");
json_append(&buf, i < ws->block_count - 1 ? " },\n" : " }\n");
}
json_append(&buf, " ],\n");
@@ -123,20 +146,270 @@ char* serializer_save_to_string(BlockWorkspace* ws) {
return buf.buffer;
}
// 跳过空白字符
static const char* skip_ws(const char* p) {
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') p++;
return p;
}
// 查找字符串值,返回指向引号内内容的指针,并在 *len 中设置长度
static const char* parse_json_string(const char* p, int* len) {
p = skip_ws(p);
if (*p != '"') return NULL;
p++;
const char* start = p;
while (*p && *p != '"') p++;
if (*p != '"') return NULL;
*len = (int)(p - start);
return start;
}
// 查找数字值,返回指向数字开头的指针
static const char* parse_json_number(const char* p, float* out) {
p = skip_ws(p);
const char* start = p;
if (*p == '-') p++;
while (*p >= '0' && *p <= '9') p++;
if (*p == '.') {
p++;
while (*p >= '0' && *p <= '9') p++;
}
if (p == start) return NULL;
*out = (float)strtod(start, NULL);
return p;
}
int serializer_load_from_string(BlockWorkspace* ws, const char* json_str) {
// 简化的 JSON 解析
// 完整实现需要完整的 JSON 解析器
workspace_clear(ws);
// 查找 "blocks" 数组
const char* blocks_start = strstr(json_str, "\"blocks\"");
if (!blocks_start) return 0;
// 简化解析 - 只提取基本信息
// 实际项目中应使用 cJSON 或类似库
// TODO: 实现完整的 JSON 解析
block_types_init();
// 解析 blocks 数组
const char* blocks_key = strstr(json_str, "\"blocks\"");
if (!blocks_key) return 0;
const char* p = blocks_key + 8;
p = skip_ws(p);
if (*p != ':') return 0;
p++;
p = skip_ws(p);
if (*p != '[') return 0;
p++;
while (1) {
p = skip_ws(p);
if (*p == ']') { p++; break; }
if (*p != '{') return 0;
p++;
int id = 0;
char type_name[64] = {0};
char label[64] = {0};
float x = 0, y = 0;
SlotValue input_values[MAX_BLOCK_INPUTS];
int input_count = 0;
while (1) {
p = skip_ws(p);
if (*p == '}') { p++; break; }
int key_len = 0;
const char* key = parse_json_string(p, &key_len);
if (!key) return 0;
p = key + key_len + 1;
p = skip_ws(p);
if (*p != ':') return 0;
p++;
if (key_len == 2 && strncmp(key, "id", 2) == 0) {
float fv; p = parse_json_number(p, &fv); id = (int)fv;
} else if (key_len == 4 && strncmp(key, "type", 4) == 0) {
int val_len = 0;
const char* val = parse_json_string(p, &val_len);
if (val) {
if (val_len >= (int)sizeof(type_name)) val_len = (int)sizeof(type_name) - 1;
strncpy(type_name, val, val_len);
type_name[val_len] = '\0';
p = val + val_len + 1;
}
} else if (key_len == 1 && *key == 'x') {
p = parse_json_number(p, &x);
} else if (key_len == 1 && *key == 'y') {
p = parse_json_number(p, &y);
} else if (key_len == 5 && strncmp(key, "label", 5) == 0) {
int val_len = 0;
const char* val = parse_json_string(p, &val_len);
if (val) {
if (val_len >= (int)sizeof(label)) val_len = (int)sizeof(label) - 1;
strncpy(label, val, val_len);
label[val_len] = '\0';
p = val + val_len + 1;
}
} else if (key_len == 6 && strncmp(key, "inputs", 6) == 0) {
p = skip_ws(p);
if (*p == '[') {
p++;
input_count = 0;
while (1) {
p = skip_ws(p);
if (*p == ']') { p++; break; }
if (*p != '{') break;
p++;
char iv_type[16] = {0};
float fv = 0;
int bv = 0;
char sval_str[256] = {0};
while (1) {
p = skip_ws(p);
if (*p == '}') { p++; break; }
int ik_len = 0;
const char* ik = parse_json_string(p, &ik_len);
if (!ik) break;
p = ik + ik_len + 1;
p = skip_ws(p);
if (*p != ':') break;
p++;
if (ik_len == 4 && strncmp(ik, "type", 4) == 0) {
int iv_len = 0;
const char* iv = parse_json_string(p, &iv_len);
if (iv) {
if (iv_len >= (int)sizeof(iv_type)) iv_len = (int)sizeof(iv_type) - 1;
strncpy(iv_type, iv, iv_len);
iv_type[iv_len] = '\0';
p = iv + iv_len + 1;
}
} else if (ik_len == 5 && strncmp(ik, "value", 5) == 0) {
p = skip_ws(p);
if (*p == '"') {
int sv_len = 0;
const char* sval = parse_json_string(p, &sv_len);
if (sval) {
if (sv_len >= (int)sizeof(sval_str)) sv_len = (int)sizeof(sval_str) - 1;
strncpy(sval_str, sval, sv_len);
sval_str[sv_len] = '\0';
p = sval + sv_len + 1;
}
} else if (strncmp(p, "true", 4) == 0) {
bv = 1; p += 4;
} else if (strncmp(p, "false", 5) == 0) {
bv = 0; p += 5;
} else {
p = parse_json_number(p, &fv);
}
} else {
while (*p && *p != ',' && *p != '}') p++;
}
p = skip_ws(p);
if (*p == ',') p++;
}
// 暂存输入值
if (input_count < MAX_BLOCK_INPUTS) {
SlotValue* sv = &input_values[input_count++];
memset(sv, 0, sizeof(SlotValue));
if (strcmp(iv_type, "number") == 0) {
sv->type = SLOT_VALUE_NUMBER;
sv->as.number = (double)fv;
} else if (strcmp(iv_type, "string") == 0) {
sv->type = SLOT_VALUE_STRING;
strncpy(sv->as.string, sval_str, sizeof(sv->as.string) - 1);
} else if (strcmp(iv_type, "boolean") == 0) {
sv->type = SLOT_VALUE_BOOLEAN;
sv->as.boolean = bv;
} else {
sv->type = SLOT_VALUE_NUMBER;
sv->as.number = 0;
}
}
p = skip_ws(p);
if (*p == ',') p++;
}
}
} else {
// 跳过未知值 (简单跳过直到逗号或右花括号)
while (*p && *p != ',' && *p != '}') p++;
}
p = skip_ws(p);
if (*p == ',') p++;
}
BlockType type = block_type_find_by_name(type_name);
if (type == BLOCK_TYPE_COUNT || type_name[0] == '\0') {
type = BLOCK_EVENT_START; // fallback
}
Block* block = block_create(type, x, y);
if (block) {
block->id = id;
strncpy(block->label, label, sizeof(block->label) - 1);
block->label[sizeof(block->label) - 1] = '\0';
for (int j = 0; j < input_count && j < block->input_count; j++) {
block->inputs[j].value = input_values[j];
}
workspace_add_block(ws, block);
if (id >= ws->next_block_id) ws->next_block_id = id + 1;
}
p = skip_ws(p);
if (*p == ',') p++;
}
// 解析 connections 数组
const char* conn_key = strstr(json_str, "\"connections\"");
if (conn_key) {
p = conn_key + 13;
p = skip_ws(p);
if (*p == ':') {
p++;
p = skip_ws(p);
if (*p == '[') {
p++;
while (1) {
p = skip_ws(p);
if (*p == ']') { p++; break; }
if (*p != '{') break;
p++;
int from_id = -1, to_id = -1;
while (1) {
p = skip_ws(p);
if (*p == '}') { p++; break; }
int key_len = 0;
const char* key = parse_json_string(p, &key_len);
if (!key) break;
p = key + key_len + 1;
p = skip_ws(p);
if (*p != ':') break;
p++;
float fv = 0;
if (key_len == 4 && strncmp(key, "from", 4) == 0) {
p = parse_json_number(p, &fv); from_id = (int)fv;
} else if (key_len == 2 && strncmp(key, "to", 2) == 0) {
p = parse_json_number(p, &fv); to_id = (int)fv;
} else {
while (*p && *p != ',' && *p != '}') p++;
}
p = skip_ws(p);
if (*p == ',') p++;
}
if (from_id >= 0 && to_id >= 0) {
Block* from_block = workspace_find_block(ws, from_id);
Block* to_block = workspace_find_block(ws, to_id);
if (from_block && to_block && from_block->output_count > 0 && to_block->input_count > 0) {
workspace_add_connection(ws, &from_block->outputs[0], &to_block->inputs[0]);
}
}
p = skip_ws(p);
if (*p == ',') p++;
}
}
}
}
return 1;
}