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

+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;
}