390 lines
9.8 KiB
C
390 lines
9.8 KiB
C
|
AUTOTEST {
|
||
|
test_obj_core();
|
||
|
}
|
||
|
|
||
|
// ----------------------------------------------------------------------------
|
||
|
|
||
|
typedef struct orc { OBJ
|
||
|
char name[8];
|
||
|
} orc;
|
||
|
|
||
|
//#pragma pack(push, 1)
|
||
|
typedef struct boy { OBJ
|
||
|
char name[8];
|
||
|
float hp;
|
||
|
} boy;
|
||
|
//#pragma pack(pop)
|
||
|
|
||
|
OBJTYPEDEF(orc,100)
|
||
|
int orc_ctor(orc* self) { strcpy(self->name, "orc!"); printf("i'm orc %p\n", self); return 0; }
|
||
|
int orc_tick(orc* self) { printf("%p orc tick\n", self); return 0; }
|
||
|
char* orc_save(orc *self) { return obj_saveini(self); } // PACKMSG("ss", "orc_v1", self->name); }
|
||
|
|
||
|
OBJTYPEDEF(boy,101)
|
||
|
int boy_ctor(boy* self) { strcpy(self->name, "boy!"); printf("i'm boy %p\n", self); self->hp = 0x80; return 0; }
|
||
|
int boy_tick(boy* self) { printf("%p boy tick, hp:%f\n", self, self->hp); return 0; }
|
||
|
char* boy_save(boy *self) { return obj_saveini(self); } // PACKMSG("ssf", "boy_v1", self->name, self->hp); }
|
||
|
|
||
|
AUTOTEST {
|
||
|
EXTEND(orc, ctor,tick,save);
|
||
|
EXTEND(boy, ctor,tick,save);
|
||
|
|
||
|
// instance gameobjs
|
||
|
|
||
|
boy *obj1 = obj_new(boy, "boy", 123);
|
||
|
orc *obj2 = obj_new(orc, "orc");
|
||
|
orc *obj3 = obj_new(orc, "orc");
|
||
|
|
||
|
printf("%p\n", obj_datac(obj1));
|
||
|
printf("%d vs %d vs %d\n", (int)sizeof(boy), obj_sizeof(obj1), obj_size(obj1));
|
||
|
|
||
|
// generics
|
||
|
obj_tick(obj1);
|
||
|
obj_tick(obj2);
|
||
|
obj_tick(obj3);
|
||
|
|
||
|
obj_hexdump(obj1);
|
||
|
|
||
|
obj_free(obj1); // will free
|
||
|
|
||
|
obj_ref(obj2);
|
||
|
obj_unref(obj2); // will free
|
||
|
|
||
|
obj_ref(obj3);
|
||
|
obj_free(obj3); // will do nothing
|
||
|
obj_unref(obj3); // will free
|
||
|
|
||
|
// make a dangling reference. this will be printed at end of program.
|
||
|
static int dangling;
|
||
|
obj1 = obj_new(boy, "boy", 123);
|
||
|
obj_ref(obj1);
|
||
|
}
|
||
|
|
||
|
// --- scene
|
||
|
|
||
|
AUTOTEST {
|
||
|
test_obj_scene();
|
||
|
}
|
||
|
|
||
|
// --- comps
|
||
|
|
||
|
struct WorldTravellerComponent {
|
||
|
unsigned world_source;
|
||
|
unsigned world_target;
|
||
|
};
|
||
|
|
||
|
struct TransformComponent {
|
||
|
vec3 position;
|
||
|
quat rotation;
|
||
|
vec3 scale;
|
||
|
};
|
||
|
|
||
|
struct VelocityComponent {
|
||
|
float speed;
|
||
|
};
|
||
|
|
||
|
struct LookComponent {
|
||
|
float sensitivity; // _and_polarity;
|
||
|
vec2 rotation;
|
||
|
};
|
||
|
|
||
|
struct PhysicsComponent {
|
||
|
float gravity;
|
||
|
vec3 velocity;
|
||
|
vec3 acceleration;
|
||
|
vec3 rotationVelocity;
|
||
|
vec3 rotationAcceleration;
|
||
|
};
|
||
|
|
||
|
struct RenderComponent {
|
||
|
aabb box;
|
||
|
mesh_t mesh;
|
||
|
texture_t texture;
|
||
|
};
|
||
|
|
||
|
AUTOTEST {
|
||
|
test_obj_ecs();
|
||
|
}
|
||
|
|
||
|
// generics
|
||
|
|
||
|
// --- example: new class
|
||
|
|
||
|
// declare new object
|
||
|
|
||
|
TYPEDEF_STRUCT(box,102,
|
||
|
int x,y,w,h;
|
||
|
);
|
||
|
|
||
|
// /* same as: */
|
||
|
// typedef struct box { OBJ
|
||
|
// int x,y,w,h;
|
||
|
// } box;
|
||
|
// OBJTYPEDEF(box,102)
|
||
|
|
||
|
// implement a few built-in interfaces
|
||
|
|
||
|
#define box(...) obj(box, __VA_ARGS__)
|
||
|
char* box_save(const box *b) { return obj_saveini(b); } // PACKMSG("siiii", "box_v1", b->x, b->y, b->w, b->h); }
|
||
|
bool box_load(box *b, const char* s) { return !!obj_loadini(b, s); } // char *header; return UNPACKMSG(s, "siiii", &header, &b->x, &b->y, &b->w, &b->h) && !strcmp(header, "box_v1"); }
|
||
|
int box_test(box *b) { return b->w > 0 && b->h > 0; }
|
||
|
void box_dtor(box *b) { puts("bye box!"); }
|
||
|
|
||
|
// create a new obj interface (not only for box! valid for every other obj that would extend on this)
|
||
|
|
||
|
obj_vtable(area, float, { return 0; });
|
||
|
|
||
|
#define obj_area(o,...) obj_method(area, o, ##__VA_ARGS__)
|
||
|
|
||
|
// implement area interface for box object
|
||
|
|
||
|
float box_area(box *b) { return b->w * b->h; }
|
||
|
|
||
|
AUTOTEST {
|
||
|
// reflect
|
||
|
STRUCT(box,int,x);
|
||
|
STRUCT(box,int,y);
|
||
|
STRUCT(box,int,w);
|
||
|
STRUCT(box,int,h);
|
||
|
|
||
|
// extend
|
||
|
obj_extend(box,save);
|
||
|
obj_extend(box,load);
|
||
|
obj_extend(box,test);
|
||
|
obj_extend(box,dtor);
|
||
|
obj_extend(box,area);
|
||
|
|
||
|
// -- example
|
||
|
|
||
|
box b = box(0,0,2,3);
|
||
|
box *c = obj_new(box, 1,1,3,4);
|
||
|
|
||
|
test( obj_test(&b) );
|
||
|
test( obj_test(c) );
|
||
|
|
||
|
test( obj_area(&b) == 6 );
|
||
|
test( obj_area(c) == 12 );
|
||
|
|
||
|
// serialization tests
|
||
|
test_obj_serialization(&b, c);
|
||
|
|
||
|
test_obj_similarity(&b, c);
|
||
|
char *sav = obj_save(c);
|
||
|
obj_load(&b, sav);
|
||
|
// obj_load(&b, obj_save(c)); // @fixme: this expression in mingw
|
||
|
test_obj_equality(&b, c);
|
||
|
}
|
||
|
|
||
|
// --- reflection
|
||
|
|
||
|
typedef struct MyVec3 { OBJ
|
||
|
float x,y,z;
|
||
|
} MyVec3;
|
||
|
|
||
|
OBJTYPEDEF(MyVec3,77)
|
||
|
|
||
|
typedef struct MyTransform {
|
||
|
MyVec3 location; ///R @todo: ideally, we would want self-reflection to inscribe these members
|
||
|
MyVec3 rotation; ///R @todo: ideally, we would want self-reflection to inscribe these members
|
||
|
float scale; ///R @todo: ideally, we would want self-reflection to inscribe these members
|
||
|
} MyTransform;
|
||
|
|
||
|
/*
|
||
|
// @todo: which is technically like doing
|
||
|
AUTORUN {
|
||
|
STRUCT(MyTransform, MyVec3, location);
|
||
|
STRUCT(MyTransform, MyVec3, rotation);
|
||
|
STRUCT(MyTransform, float, scale);
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
OBJTYPEDEF(MyTransform,78);
|
||
|
|
||
|
typedef struct MyObject { OBJ
|
||
|
char *id;
|
||
|
int spawnx,spawny;
|
||
|
float time;
|
||
|
struct MyObject *next;
|
||
|
MyTransform tr;
|
||
|
} MyObject;
|
||
|
|
||
|
OBJTYPEDEF(MyObject,79);
|
||
|
|
||
|
|
||
|
AUTOTEST {
|
||
|
STRUCT( MyVec3, float, x, "Right" );
|
||
|
STRUCT( MyVec3, float, y, "Forward" );
|
||
|
STRUCT( MyVec3, float, z, "Up" );
|
||
|
|
||
|
STRUCT( MyTransform, MyVec3, location, "World location (absolute)" );
|
||
|
STRUCT( MyTransform, MyVec3, rotation, "Local rotation (in degrees)" );
|
||
|
STRUCT( MyTransform, float, scale, "Local scale (in centimeters)" );
|
||
|
|
||
|
STRUCT( MyObject, int, spawnx, "Actor Spawn X" );
|
||
|
STRUCT( MyObject, int, spawny, "Actor Spawn Y" );
|
||
|
STRUCT( MyObject, string, id, "Actor name" );
|
||
|
STRUCT( MyObject, MyTransform, tr, "Actor transform" );
|
||
|
STRUCT( MyObject, float, time, "Actor time" );
|
||
|
STRUCT( MyObject, MyObject *, next, "Next actor in seq" );
|
||
|
|
||
|
// reflect_print("MyVec3");
|
||
|
// reflect_print("MyTransform");
|
||
|
// reflect_print("MyObject");
|
||
|
|
||
|
// construct a type from a reflected struct
|
||
|
MyVec3 *o = obj_new(MyVec3, 1,2,-3);
|
||
|
test( 12 == obj_size(o) );
|
||
|
MyVec3 *o2 = obj_make("[MyVec3]\nfloat.y=2\nfloat.x=1\nfloat.z=-3");
|
||
|
test( !obj_comp(o,o2) ) || obj_hexdump(o), obj_hexdump(o2);
|
||
|
|
||
|
for each_objmember(o,type,name,ptr) {
|
||
|
/**/ if( !strcmp(type, "float") ) printf("%s %s = %f\n", type, name, *(float*)ptr );
|
||
|
else if( !strcmp(type, "double") ) printf("%s %s = %f\n", type, name, *(double*)ptr );
|
||
|
}
|
||
|
|
||
|
test_obj_console(o);
|
||
|
test_obj_console(o2);
|
||
|
|
||
|
// Setup objects
|
||
|
MyObject *root = obj_make("[MyObject]");
|
||
|
obj_hexdump(root);
|
||
|
|
||
|
MyObject *oo = obj_make("[MyObject]\nid=\"An identifier!\"\nx=123\ny=256\nrotation=90\nnext=root\n");
|
||
|
obj_hexdump(oo);
|
||
|
|
||
|
// Dump contents of our objects
|
||
|
|
||
|
obj_print(oo);
|
||
|
puts("---");
|
||
|
|
||
|
obj_hexdump(oo);
|
||
|
puts("---");
|
||
|
|
||
|
// Save to mem
|
||
|
|
||
|
char *sav = obj_savebin(oo);
|
||
|
test( sav && strlen(sav) > 0 );
|
||
|
|
||
|
// Clear
|
||
|
|
||
|
obj_zero(oo);
|
||
|
obj_hexdump(oo);
|
||
|
puts("---");
|
||
|
|
||
|
// Reload
|
||
|
|
||
|
obj_loadbin(oo, sav);
|
||
|
obj_hexdump(oo);
|
||
|
}
|
||
|
|
||
|
// --- Benchmarks for call overhead.
|
||
|
|
||
|
AUTOTEST {
|
||
|
// Here, we're using a blank ctor call as a method to test/stress call overhead.
|
||
|
//
|
||
|
// results (old i5-4300/1.90Ghz laptop):
|
||
|
// v1: 427 million calls/s. compiled with "cl /Ox /Os /MT /DNDEBUG /GL /GF /arch:AVX2"
|
||
|
// v2: 450 million calls/s. compiled with "cl /Ox /O2 /MT /DNDEBUG /GF /arch:AVX2"
|
||
|
|
||
|
double t;
|
||
|
enum { N = 100000000 };
|
||
|
|
||
|
t = -time_ss();
|
||
|
MyVec3 o = obj(MyVec3, 1,2,3); obj_setname(&o, "MyVec3");
|
||
|
for( unsigned i = 0; i < N; ++i ) {
|
||
|
obj_ctor(&o);
|
||
|
}
|
||
|
t += time_ss();
|
||
|
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
|
||
|
|
||
|
t = -time_ss();
|
||
|
MyVec3 *op = obj_new(MyVec3, 1,2,3);
|
||
|
for( unsigned i = 0; i < N; ++i ) {
|
||
|
obj_ctor(op);
|
||
|
}
|
||
|
t += time_ss();
|
||
|
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
|
||
|
}
|
||
|
|
||
|
// --- metas
|
||
|
|
||
|
AUTOTEST {
|
||
|
box b = box(1,2,3,4);
|
||
|
|
||
|
test( !strcmp("box", obj_type(&b)) );
|
||
|
test( !strcmp("box", obj_name(&b)) );
|
||
|
|
||
|
b = box(1,2,3,4);
|
||
|
obj_setname(&b, "MyBox1");
|
||
|
|
||
|
test( !strcmp("box", obj_type(&b)) );
|
||
|
test( !strcmp("MyBox1", obj_name(&b)) );
|
||
|
|
||
|
test_obj_metadatas(&b);
|
||
|
|
||
|
obj_free(&b);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
TYPEDEF_STRUCT(test_vec3_serial, __COUNTER__,
|
||
|
vec3 position;
|
||
|
vec3 accel;
|
||
|
);
|
||
|
AUTOTEST {
|
||
|
STRUCT(test_vec3_serial, vec3, position);
|
||
|
STRUCT(test_vec3_serial, vec3, accel);
|
||
|
test_vec3_serial v = obj(test_vec3_serial, {1,2,3},{4,5,6}), z = obj(test_vec3_serial);
|
||
|
test(obj_comp(&v,&z) != 0) || obj_print(&v) & obj_print(&z);
|
||
|
obj_loadini(&z, obj_saveini(&v));
|
||
|
test(obj_comp(&v,&z) == 0) || obj_print(&z);
|
||
|
}
|
||
|
|
||
|
TYPEDEF_STRUCT(MyObject2, __COUNTER__,
|
||
|
const char* id;
|
||
|
int x,y;
|
||
|
float rotation;
|
||
|
struct MyObject2 *next;
|
||
|
);
|
||
|
AUTORUN {
|
||
|
STRUCT(MyObject2, const char *, id);
|
||
|
STRUCT(MyObject2, int, x);
|
||
|
STRUCT(MyObject2, int, y);
|
||
|
STRUCT(MyObject2, float, rotation);
|
||
|
STRUCT(MyObject2, struct MyObject2*, next);
|
||
|
|
||
|
// Construct two objects
|
||
|
MyObject2 *root = obj_new(MyObject2);
|
||
|
MyObject2 *o = obj_new(MyObject2, "An identifier!", 0x11, 0x22, 3.1415f, root );
|
||
|
// Copy tests
|
||
|
{
|
||
|
printf("%d vs %d vs %d\n", (int)sizeof(MyObject2), obj_size(o), (int)sizeof(obj) + obj_size(o) + (int)sizeof(array(void*)));
|
||
|
MyObject2 *clone = obj_clone(o);
|
||
|
test(obj_comp(clone,o) == 0) || obj_print(o) & obj_print(clone) & obj_hexdump(o) & obj_hexdump(clone);
|
||
|
test(obj_free(clone) == 0);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
MyObject2 *copy = obj_new(MyObject2);
|
||
|
test(obj_copy(copy, o));
|
||
|
test(obj_comp(copy,o)==0) || obj_print(copy);
|
||
|
test(obj_free(copy) == 0);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
MyObject2 *copy = obj_new(MyObject2, "A different identifier!", 0x33, 0x44, 0.0f, root );
|
||
|
test(obj_copy(copy, o));
|
||
|
test(obj_comp(copy,o)==0) || obj_print(copy);
|
||
|
test(obj_free(copy) == 0);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
void *copy = obj_malloc(100); // untyped class
|
||
|
obj_mutate(copy, o);
|
||
|
obj_print(copy);
|
||
|
obj_copy(copy, o);
|
||
|
obj_print(copy);
|
||
|
obj_free(copy);
|
||
|
}
|
||
|
}
|