Skip to content

ERA-C Overview

Introduction

Warning

This page assumes you are already familiar with basic programming concepts and data types.

ERA-3D games are programmed in a scripting language called ERA-C. ERA-C is similar to C in many ways, but has a few key differences that are worth noting.

If you are already familiar with C, see the Differences From C section.

Comments

Comments in ERA-C follow the same syntax as C/C++.

// single-line comment
int a = 32; // another comment
/*
multi
line
comment
*/

Multi-line comments can be nested.

/*
  this is a comment
  /*
    this is a nested comment
  */
  this is still a comment
*/

Variables

Both local and global variables are declared using the pattern <data type> <name> ;.

Variable names must start with a letter and can contain the characters a-z, A-Z, 0-9, _

int my_int; // an int variable
float my_float; // a float variable

Local variables (variables declared inside a function or loop scope) can be assigned a value when they are declared.

int foo = 42;

Global variables must be initialized inside of a function.

The init() function is a good place for that. (See the Hook Functions section)

Primitive Types

Primitive types are data types built into the ERA-C compiler.

int

int is a data type that is capable of storing positive and negative whole numbers.

Specifically, they are 32-bit integers. Most operations treat them as signed integers, but some operators treat int values as unsigned.

Integer literal values can be given in decimal form, but they can also be represented in hexadecimal and binary form using the 0x and 0b prefixes.

Unlike C, leading zeros do not mean a number is in octal format.

int a = 24; // positive value
int b = -24; // negative value
int hex = 0xFF; // hexadecimal value (255)
int binary = 0b10101010; // binary value (170)

// to make large numbers easier to read, underscores can be inserted between digits.
int big_number = 12_345_678;

// character literals are also supported, and are interpreted as int values.
int letter_e = 'E';

ERA-C has a special syntax for encoding note values used by the soundchip.

The 0n prefix followed by a note name (see list below) and an octave number (0-8) define a note.

int c_sharp_4 = 0nC#4; // soundchip note value (note C#, octave 4)

All valid note names are listed below. Notes can be written using upper or lower case.

C, C#, D, D#, E, F, F#, G, G#, A, A#, B

ints support various arithmetic operations, as well as bitwise and logical ones.

+, -, *, /, and % are the addition, subtraction, multiplication, division, and modulus operators. They follow the same precedence rules as C.

int a = 2 + 2;
int b = 5 - 3;
int c = 9 * 100;
int d = 10 / 2;
int e = 10 % 5;

Info

Dividing int values does not result in a fractional value, the fractional component is discarded instead.

5 / 2 returns 2, not 2.5.

ERA-C also supports bitwise operations for integers.

int a = ~0b00001111; // bitwise NOT: a = 0b11110000
int b = 0b10101010 | 0b01010101; // bitwise OR: b = 0b11111111
int c = 0b10101111 & 0b00111100; // bitwise AND: c = 0b00101100
int d = 0b11001100 ^ 0b10101010; // bitwise XOR: d = 0b01100110
int e = 0b00001111 << 2; // bitwise left shift: e = 0b00111100
int f = 0b11110000 >> 4; // bitwise right shift (logical): f = 0b00001111
int g = 0b10000000 >>> 4; // bitwise right shift (arithmetic): f = 0b11111000

The logical and comparison operators return an integer value, so their result can be stored in an int.

If the comparison is true, a value of 1 is returned. Otherwise, 0 is returned.

int a = 1 < 2; // a = 1 (true)
int b = 1 == 2; // b = 0 (false)

float

floats are 32-bit floating point values, meaning they are numbers with a fractional component.

float my_float = 2.5;

floats support the +, -, *, /, and % operators, as well as the comparison operators.

vec2

vec2 represents a 2D vector with x and y components.

x and y both have the float data type.

vec2 v = vec2(1.0, 2.0); // 2D vector with the value (x=1.0, y=2.0)

v.x = 9.0; // assigning a new x value
v.y = 0.25; // assigning a new y value

The following table lists special functions used to construct vec2s with common values:

Function vec2 Return Value
vec2Zero() {0.0, 0.0} (equivalent to vec2() with no arguments)
vec2One() {1.0, 1.0}
vec2Up() {0.0, -1.0}
vec2Down() {0.0, 1.0}
vec2Left() {-1.0, 0.0}
vec2Right() {1.0, 0.0}

vec3

vec3 represents a 3D vector with x, y, and z components.

x, y, and z all have the float data type.

vec3 v = vec3(1.0, 2.0, 3.0); // 3D vector with the value (x=1.0, y=2.0, z=3.0)

v.x = 7.5; // assigning a new x value
v.y = 0.75; // assigning a new y value
v.z = 4.9; // assigning a new z value

The following table lists special functions used to construct vec3s with common values:

Function vec3 Return Value
vec3Zero() {0.0, 0.0, 0.0} (equivalent to vec3() with no arguments)
vec3One() {1.0, 1.0, 1.0}
vec3Up() {0.0, 1.0, 0.0}
vec3Down() {0.0, -1.0, 0.0}
vec3Left() {-1.0, 0.0, 0.0}
vec3Right() {1.0, 0.0, 0.0}
vec3Forward() {0.0, 0.0, -1.0}
vec3Back() {0.0, 0.0, 1.0}

Both vec2 and vec3 support addition, subtraction, multiplication, and division.

Vector types cannot be mixed in vector arithmetic, only vectors of the same dimensions can be added, subtracted, etc.

During a arithmetic operation, the individual members of the vector are operated on. For example, when adding two vec2 values, the result will be vec2(a.x + b.x, a.y + b.y). The same pattern applies to all arithmetic operators.

vec2 v2 = vec2(1.1, 2.2) + vec2(3.3, 4.4); // v2 = vec2(4.4, 6.6)
vec3 v3 = vec3(1.1, 2.2, 3.3) + vec3(4.4, 5.5, 6.6); // v3 = vec3(5.5, 7.7, 9.9)

Vectors can also be multiplied and divided by int and float values. This will return a vector with each member having been multiplied/divided by the int/float value.

vec3 a = vec3(1.1, 2.2, 3.3) * 2; // a = vec3(2.2, 4.4, 6.6)
vec2 b = vec2(5.0, 10.0) / 2.5; // b = vec2(2.0, 4.0)

Vectors support all comparison operators, and can only be compared with their same type.

Comparing vectors compares each member pair, and the comparison is only true if all pairs satisfy the comparison.

vec2 a = vec2(10.0, 20.0);
vec2 b = vec2(1.0, 2.0);

int result = a < b; // result = 1 (true), as a.x < b.x and a.y < b.y

string

The string data type is used to hold text.

string literals must be enclosed within double-quotes (").

string text = "hello world!";

To fetch a character from the string, or to change a character, use the following expression: <variable> [ <index> ];.

The characters of a string are zero-indexed, meaning that the first character is stored at index 0.

string text = "hello world!";
int character = text[0]; // fetch a character

Internally, string is a struct (see the Structs section) that looks like this:

struct string {
  int length;
  void* data;
};

Warning

String literals allocate space in cartridge ROM to store their data, so trying to write characters into a string initialized using a literal will cause an error and halt the program.

If a modifiable string is needed, a string struct can be manually created to point to somewhere in RAM.

Strings support the == and != operators. Two strings are considered equal if the following conditions are met:

  1. both strings are the same length
  2. both strings point to the same address or the characters in both strings match

Arrays

Arrays are essentially blocks of memory used to store a list of elements all of a particular data type. For example, you might have an array containing 10 int values, or an array containing 5 vec2 values.

Arrays are fixed in size, once they are declared the cannot be resized.

int my_array[10]; // declare an array of 10 ints

Access array elements using the pattern <array variable> [ <index> ], where <index> is an integer value.

Array values are zero-indexed; meaning the first element in the array is stored at index 0, the second at index 1, and so on.

int my_array[10];
int x = my_array[0]; // get the first value stored in the array
my_array[1] = 99; // store a new value in the array at index 1

Pointers

ERA-C supports pointers, which behave similarly to pointers in C. To declare a pointer to a type, put a * after the data type.

int* int_pointer; // pointer to an int

To obtain the memory address of a variable, use the address-of operator. (&)

int x = 42; // normal int variable
int* ptr = &x; // ptr now contains the memory address of x 

The unary dereference operator (*) can be used to fetch the value pointed to by a pointer.

int x = 42;
int* ptr = &x;
int y = *ptr; // y now contains the same value as x

Like arrays, pointers can be used to access multiple contiguous values stored next to each other in memory.

int* p = 0; // in this example, p points to memory address 0, which corresponds to general-purpose memory.
int first = p[0]; // get the first value stored at the address p points to
p[1] = 100; // store a new value at the address p points to plus an offset of 1 int. (which would be the address 4. 1 int = 4 bytes)

A pointer with the type void* is a special type of pointer that can point to any data type. Pointers of this type are called "void pointers".

Void pointers cannot be dereferenced, they are only used as a container for arbitrary memory addresses.

Any pointer type and be assigned the value of a void pointer, and vice versa.

int i = 100;
float f = 32.2;
void* p = &i; // p points to i
int x = *p; // ERROR: cannot dereference void pointers!
p = &f; // p now points to f

Pointers can be assigned arbitrary integer values, and thus arbitrary memory addresses.

In C, this is dangerous and discouraged most of the time, but in ERA-C it is needed to access specific parts of memory.

int* p = 0x00800000; // p now points to address 0x00800000, which is the beginning of the system's texture memory

Warning

Pointers in ERA-C must be aligned when dereferencing them, meaning the address they contain must be a multiple of the size of the type they point to (or address 0). If a pointer is misaligned when it is dereferenced, en error will be raised and the program will halt.

Pointers can be added to or subtracted from, changing the address they point to. This is referred to as "pointer arithmetic".

Adding a value of 1 to a pointer will increase its address by the size of the data type it points to.

For example, adding 1 to a int* will increase its address by 4, since ints are 4 bytes in size.

Info

Void pointers do not support pointer arithmetic.

int* p = 0;
p = p + 1; // p now points to the next int in memory

The sizeof() operator will return the size of a given type in bytes.

int size = sizeof(int); // size = 4

null is a special constant that represents an invalid memory address. Unlike C's NULL, it is not equal to the integer value 0.

Assign a pointer to null to signify that it does not currently point to a valid address.

void* p = null;

Structs

Structs are compound data types that are made up of other smaller types. They are useful for representing complex objects with many attributes.

struct MyStruct {
  int a;
  float b;
  int c;
};

In the above example, the struct MyStruct is declared, and contains an int and float members.

Info

Structs must be declared outside of any function, and before they are used as a variable type.

To create a struct instance variable, use the struct name as the variable type.

MyStruct instance;

Struct instances can be created using the pattern <struct name> ( <member values...> )

Mystruct instance = Mystruct(1, 2.5, 3); // create a MyStruct instance with the value (a=1, b=2.5, c=3)

C-style struct initializers are also allowed.

These use the pattern ( <struct name> ) { <member values...> }

Mystruct instance = (Mystruct){1, 2.5, 3}; // create a MyStruct instance with the value (a=1, b=2.5, c=3)

If a struct initializer's member value list is left empty, all struct members will be initialized to zero.

MyStruct instance = MyStruct(); // instance = (0, 0.0, 0)
vec3 v = vec3(); // v = (0.0, 0.0, 0.0)

To access struct members, use the dot operator. (.)

Mystruct instance = MyStruct(1, 2.5, 3);
int x = instance.a; // x now contains 1
instance.b = 10.1; // set the b member of instance to a new value

When accessing a struct using a pointer, the arrow operator (->) must be used instead of the dot operator.

Mystruct instance = MyStruct(1, 2.5, 3);
MyInstance* p = &instance; // p points to instance
p -> a = 22; // access the a member of instance using the pointer p

Function Pointers

Function pointers are special pointers that point to code, rather than data.

To declare a function pointer, use the pattern <return type> ( <argument types...> ) <variable name> ;

void(int, float) func_ptr; // func_ptr points to a function that accepts an `int` and `float` argument and does not return a value.

Assign a function pointer by using the name of the function to point to.

void my_function(int a, float b) {
  // ... function code here ...
}

void(int, float) func_ptr = my_function;

Function pointers do not support pointer arithmetic, and can only be cast to void*.

Function pointers can be assigned null.

Type Casting

Type casting in ERA-C works much like C. To cast an expression to another type, use the pattern ( <type> ) <expression>.

float f = 2.5;
int i = (int)f; // cast the float value of f to an int

float to int casting discards the fractional component of the float. (The value is truncated)

In the above example, i would have the value 2 after the cast.

The following table lists all accepted casts:

Original Type Target Type Result
int float float equal to the original integer value.
int Non-function pointer Pointer with address equal to the original integer value.
float int int containing the truncated integer value of the float.
Non-function pointer int int containing the address of the original pointer value.
Function pointer void* void* containing the address of the original function pointer value.
void* Function pointer Function pointer containing the address of the original void pointer value.

Enums

Enums are lists of compile-time constants used to represent numerical values.

// declaring an enum
enum {
  VALUE_A = 0, // enum entries can be assigned direct values
  VALUE_B, // when no value is given, an entry is assigned a value that is 1 higher than the previous entry
  VALUE_C
};

// using the enum entries
int x = VALUE_C; // x = 2

Info

Enums must be declared outside of any function, and before their members are used in an expression.

Operators

ERA-C has the following operators:

Operator Description
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulus
! Logical Not (Unary)
&& Logical And
|| Logical Or
== Equal-to Comparision
!= Not-Equal-to Comparision
< Less-Than Comparision
> Greater-Than Comparision
<= Less-Than-or-Equal-to Comparision
>= Greater-Than-or-Equal-to Comparision
~ Bitwise NOT (Unary)
& Bitwise AND
| Bitwise OR
^ Bitwise XOR
<< Bitwise Left Shift
>> Bitwise Logical Right Shift
>>> Bitwise Arithmetic Right Shift
. Struct Member Access
-> Struct Member Access (Pointer)
sizeof() Type Size (in bytes)

Control Flow

if statements work just like in C.

int x = 1;

if (x == 1) {
  // do something...
}
else if (x == 2) {
  // do something else...
}
else {
  // no previous conditions were true
}

int values can be used as conditions. A value of 0 is false, all non-zero values are true.

Pointers and function pointers can be used as the if statement condition to check if they point to valid addresses.

void* p = null;

if (p) {
  // p points to a valid address
}
else {
  // p is not valid
}

Info

The expression my_pointer == null will only be true if my_pointer contains exactly the value null.

Loops

ERA-C supports for and while loops with the same syntax as C.

for (int i = 0; i < 10; i = i + 1) {
  // do something...
}
while (a == b) {
  // do something...
}

The break keyword will end the current loop immediately.

for (int i = 0; i < 10; i = i + 1) {
  if (i == 5) {
    break; // exit the loop body
  }

  // do something with i...
}

The continue keyword will skip to the next loop iteration.

for (int i = 0; i < 10; i = i + 1) {
  if (i == 5) {
    continue; // the rest of the loop body is skipped, and the loop starts again after incrementing i
  }

  // do something with i...
}

Functions

Functions in ERA-C follow mostly the same syntax as C.

// declaring a function
void my_function(int a, float b) {
   // do something...
}

// calling the function
my_function(1, 2.0);

The return keyword returns a value from a function.

int add(int a, int b) {
   return a + b;
}

Info

Unlike C, a function that lists no arguments is a function that accepts no arguments.

The C style void my_function(void) will not compile in ERA-C.

Vararg Functions

Functions that accept a variable number of arguments are called "vararg" or "variadic" functions.

The special vararg type (...) is used to indicate that a function is variadic.

The vararg type must come after all other non-optional arguments.

// declaring a variadic function
void my_function(int a, float b, ...) {
  // do something...
}

// calling the function
my_function(1, 2.0, 3, 4, 5);

To access vararg arguments, use the vargc() and vargv() API functions.

vargc() takes no arguments and returns the remaining number of bytes available in the varargs list.

if (vargc() >= 4) {
  // there are at least 4 bytes of vararg data left.
}

vargv() returns a void* pointing to the current vararg argument, and accepts a size in bytes as an argument.

The size given to vargv() is the offset to give the vararg pointer in order to move past the current argument.

It is recommended to use sizeof() to get argument size.

int sum(...) {
  int result = 0;
  while (vargc() >= sizeof(int)) {
    int* arg_pointer = (int*) vargv(sizeof(int));
    result = result + *arg_pointer;
  }
  return result;
}

// calling sum()
int value = sum(1, 2, 3, 4, 5); // value = 15

The above snippet is an example of a simple sum function that adds a variable number of integers.

Hook Functions

ERA-3D recognizes special "hook" functions declared in a ERA-C program and will call them at certain times.

At least one hook function must be declared in order for a cart to run.

init

if a function is declared with the signature void init(), it will be called once when a cart is ran.

Use this function to initialize game state.

void init() {
  // initialize state here...
}

update

if a function is declared with the signature void update(float delta_time), it will be called once every frame.

Use this function to update game state. (check inputs, move objects, etc.)

delta_time will be passed to the function automatically, and will contain the amount of time (in seconds) since the last frame.

void update(float delta_time) {
  // update state here...
}

Info

The delta_time argument for update() can be given any other name, but it must be a float.

draw

if a function is declared with the signature void draw(), it will be called once every frame after the call to update().

Use this function to configure rendering settings and to render graphics to the screen.

void draw() {
  // render graphics here...
}

Built-in Constants

The ERA-C compiler has several built-in constants for use with various functions. The following table lists their names and values:

Name Value
true 1
false 0
null 0xFFFFFFFF
BLEND_ALPHA 0
BLEND_ADD 1
BLEND_MULTIPLY 2
BLEND_ADD_ALT 3
BLEND_SUBTRACT 4
BLEND_PREMULTIPLY 5
BLEND_CUSTOM 6
BLEND_CUSTOM_EX 7
BTN_UP 0
BTN_DOWN 1
BTN_LEFT 2
BTN_RIGHT 3
BTN_TRIANGLE 4
BTN_CROSS 5
BTN_SQUARE 6
BTN_CIRCLE 7
BTN_L1 8
BTN_L2 9
BTN_R1 10
BTN_R2 11
BTN_SELECT 12
BTN_START 13
BTN_MOUSE_LEFT 0
BTN_MOUSE_RIGHT 1
BTN_MOUSE_MIDDLE 2
CAM_PERSPECTIVE 0
CAM_ORTHOGRAPHIC 1
CLEAR_NONE 0
CLEAR_COLOR 0b100
CLEAR_DEPTH 0b010
CLEAR_STENCIL 0b001
CLEAR_ALL 0b111
COLOR_NONE 0
COLOR_R 0b1000
COLOR_G 0b0100
COLOR_B 0b0010
COLOR_A 0b0001
COLOR_ALL 0b1111
CULL_BACK 0
CULL_FRONT 1
CULL_NONE 2
EQ_ADD 0
EQ_SUBTRACT 1
EQ_SUBTRACT_REVERSE 2
EQ_MIN 3
EQ_MAX 4
FACE_FRONT 0
FACE_BACK 1
FACE_BOTH 2
FACTOR_ZERO 0
FACTOR_ONE 1
FACTOR_SRC_RGB 2
FACTOR_ONE_MINUS_SRC_RGB 3
FACTOR_DST_RGB 4
FACTOR_ONE_MINUS_DST_RGB 5
FACTOR_SRC_ALPHA 6
FACTOR_ONE_MINUS_SRC_ALPHA 7
FACTOR_DST_ALPHA 8
FACTOR_ONE_MINUS_DST_ALPHA 9
FACTOR_CONSTANT_RGB 10
FACTOR_ONE_MINUS_CONSTANT_RGB 11
FACTOR_CONSTANT_ALPHA 12
FACTOR_ONE_MINUS_CONSTANT_ALPHA 13
FACTOR_SRC_ALPHA_SATURATE 14
FONT_WIDTH 6
FONT_HEIGHT 9
FUNC_LESS 0
FUNC_LEQUAL 1
FUNC_GREATER 2
FUNC_GEQUAL 3
FUNC_EQUAL 4
FUNC_NOTEQUAL 5
FUNC_ALWAYS 6
FUNC_NEVER 7
LIGHT_POINT 0
LIGHT_DIRECTIONAL 1
LOOP_OFF 0
LOOP_FORWARD 1
LOOP_PINGPONG 2
LOOP_RANGE 3
MAT_PROJECTION 0
MAT_MODELVIEW 1
MEMORY_HEAP 0x00000000
MEMORY_TEXMEM 0x00800000
MEMORY_OBJMEM 0x00C00000
MEMORY_AOBMEM 0x00F60000
MEMORY_SYSMEM 0x00F62000
MEMORY_OBJMAP 0x00F62054
MEMORY_WAVMAP 0x00F63854
MEMORY_WAVMEM 0x01000000
MEMORY_SEQMEM 0x01700000
MEMORY_TEXBANK0 0x01800000
MEMORY_TEXBANK1 0x01C00000
MEMORY_TEXBANK2 0x02000000
MEMORY_TEXBANK3 0x02400000
MEMORY_OBJBANK0 0x02800000
MEMORY_OBJBANK1 0x02B60000
MEMORY_OBJBANK2 0x02EC0000
MEMORY_OBJBANK3 0x03220000
MEMORY_OMPBANK0 0x03580000
MEMORY_OMPBANK1 0x03581800
MEMORY_OMPBANK2 0x03583000
MEMORY_OMPBANK3 0x03584800
MEMORY_WMPBANK0 0x036C0000
MEMORY_WMPBANK1 0x036C2000
MEMORY_WAVBANK0 0x03800000
MEMORY_WAVBANK1 0x03F00000
MEMORY_SEQBANK0 0x04600000
MEMORY_SEQBANK1 0x04640000
MEMORY_SEQBANK2 0x04680000
MEMORY_SEQBANK3 0x046C0000
MEMORY_SEQBANK4 0x04700000
MEMORY_SEQBANK5 0x04740000
MEMORY_SEQBANK6 0x04780000
MEMORY_SEQBANK7 0x047C0000
MEMORY_ROM 0x04800000
MEMORY_MEMCARD 0x05800000
MESH_LINES 0
MESH_TRIANGLES 1
MESH_QUADS 2
POLY_POINT 0
POLY_LINE 1
POLY_FILL 2
SCREEN_WIDTH 640
SCREEN_HEIGHT 360
STENCIL_KEEP 0
STENCIL_REPLACE 1
STENCIL_INC 2
STENCIL_INC_WRAP 3
STENCIL_DEC 4
STENCIL_DEC_WRAP 5
STENCIL_ZERO 6
STENCIL_INVERT 7
TEXTURE_WRAP 0
TEXTURE_CLAMP 1
TEXTURE_NONE 2

Built-in Structs

ERA-C has multiple built-in struct types for representing various objects. Unlike the vector and string types, which behave like structs in certain situations, these built-in structs do not have any custom functionality built into the compiler.

matrix

The matrix struct is a 4x4 matrix used for storing transformations like translation, rotation, and scale.

TODO: explain member ordering

struct matrix {
  float scale_x;  float shear_yx; float shear_zx; float translate_x; // first row
  float shear_xy; float scale_y;  float shear_zy; float translate_y; // second row
  float shear_xz; float shear_yz; float scale_z;  float translate_z; // third row
  float wx;       float wy;       float wz;       float ww;          // fourth row
};

vertex

The vertex struct contains 3D vertex data according to the order that vertex data is stored in OBJMEM and OBJBANKs.

Thus, OBJMEM and OBJBANKs can be treated as arrays of vertex structs.

struct vertex {
  vec3 position;
  vec3 normal;
  vec2 uv;
  int color;
};

cam2d

In SYSMEM, there are 4 memory-mapped 2D cameras that can be used for configuring the view.

To get a pointer to a 2D camera, use the getCam2D() function.

cam2d* cam = getCam2D(0); // get a pointer to 2D camera 0

To configure the view to a built-in 2D camera, use the camera2D() function.

camera2D(0); // use 2D camera 0

Info

When camera2D() is called, depth testing is automatically disabled.

The cam2d struct has the following definition:

struct cam2d {
  vec2 offset;
  vec2 target;
  float rotation;
  float zoom;
};

cam3d

In SYSMEM, there are 4 memory-mapped 3D cameras that can be used for configuring the view.

To get a pointer to a 3D camera, use the getCam3D() function.

cam3d* cam = getCam3D(0); // get a pointer to 3D camera 0

To configure the view to a built-in 3D camera, use the camera3D() function.

camera3D(0); // use 3D camera 0

Info

When camera3D() is called, depth testing is automatically enabled.

The cam3d struct has the following definition:

struct cam3d {
  vec3 position;
  vec3 target;
  vec3 up;
  float fov;
  int projection;
};

light

In SYSMEM, there are 8 memory-mapped lights that are used when lighting is enabled.

To get a pointer to a light, use the getLight() function.

light* light0 = getLight(0); // get a pointer to light 0

The light struct has the following definition:

struct light {
  int enabled;
  int type;
  float radius;
  int color;
  vec3 position;
  vec3 direction;
};

objentry

The Object Map (OBJMAP) contains 512 objentry structs.

To get a pointer to an entry, use the getObjEntry() function.

objentry* entry = getObjEntry(0); // get a pointer to OBJMAP entry 0

The objentry struct has the following definition:

struct objentry {
  int type;
  int start;
  int count;
};

waventry

The Wave Map (WAVMAP) contains 512 waventry structs.

To get a pointer to an entry, use the getWavEntry() function.

waventry* entry = getWavEntry(0); // get a pointer to WAVMAP entry 0

The waventry struct has the following definition:

struct waventry {
  int sample_start;
  int sample_end;
  int loop_start;
  int loop_end;
};

Collision Objects

Warning

The physics system is very experimental and subject to change.

Currently, collision detection is only implemented for colaabb vs colaabb.

ERA-3D has built-in API functions for 3D collision detection. These API functions accept pointers to collision object structs to know what objects to test.

Each collision object struct contains a type integer member that must be set to the correct ID value in order for the collision functions to properly identify them.

The compiler will automatically initialize the type member of a collision object struct to the correct ID when the struct is created, and does not need to be explicitly set by the user.

colaabb hitbox = colaabb(vec3(), 1.0, 1.0, 1.0); // the value for type is excluded and implicitly set

The following table lists all collision object IDs:

ID Collision Object Struct
0 colpoint
1 colaabb
2 colsphere
3 colcylinder
4 coltriangle

To check for collisions using these structs, use the collide() API function.

colaabb box1 = colaabb(vec3(), 1.0, 0.5, 1.0);
colaabb box2 = colaabb(vec3(), 0.5, 1.0, 0.5);

if (collide(&box1, &box2)) {
  // box1 and box2 are colliding!
  // handle collision here...
}

colpoint

A colpoint represents a single point in 3D space.

struct colpoint {
  int type;
  vec3 position;
};

colaabb

A colaabb represents an Axis Aligned Bounding Box, a 3D box that cannot be rotated.

struct colaabb {
  int type;
  vec3 position;
  float width;
  float height;
  float depth;
};

colsphere

A colsphere represents a 3D sphere.

struct colsphere {
  int type;
  vec3 position;
  float radius;
};

colcylinder

A colcylinder represents a 3D cylinder.

struct colcylinder {
  int type;
  vec3 position;
  float height;
  float radius;
};

coltriangle

A coltriangle represents a 3D triangle.

struct coltriangle {
  int type;
  vec3 point1;
  vec3 point2;
  vec3 point3;
};

Differences From C

The following table lists the main differences between C and ERA-C semantics.

C ERA-C
Multi-line comments cannot be nested Multi-line comments can be nested
Multiple integer and floating-point types of various sizes Only int and float, both 32-bit (4 bytes)
Leading zeros indicate octal format Leading zeros do not indicate octal format, and are ignored
Pointer <-> Integer conversion is not generally recommended Pointer <-> int is 100% legal
NULL null
NULL == 0 null == 0xFFFFFFFF
void foo() accepts any number of arguments void foo() does not accept any arguments
void foo(void) does not accept any arguments void foo(void) is a compile error
main() is the program starting point Hook functions init(), update(), and draw() are called periodically