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:
1 parent
2653926d85
commit
0669d102ce
35 files changed
+1844
-314
No files matched your search
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user