add x parser
This commit is contained in:
parent
e389a0c80c
commit
4e20ade922
118
x/lex.py
Normal file
118
x/lex.py
Normal file
@ -0,0 +1,118 @@
|
||||
import string
|
||||
|
||||
def parse_magic(mem, offset):
|
||||
magic = b"xof 0302txt 0064"
|
||||
window = bytes(mem[offset:offset+len(magic)])
|
||||
assert window == magic, window
|
||||
return offset + len(magic)
|
||||
|
||||
string_digits = set(ord(i) for i in string.digits)
|
||||
|
||||
def parse_number(mem, offset):
|
||||
mem = memoryview(mem)
|
||||
whole = []
|
||||
fraction = []
|
||||
sign = 1
|
||||
if mem[offset] == ord('-'):
|
||||
sign = -1
|
||||
offset += 1
|
||||
# whole
|
||||
while True:
|
||||
c = mem[offset]
|
||||
if c in string_digits:
|
||||
whole.append(c)
|
||||
offset += 1
|
||||
elif c == ord('.'):
|
||||
assert whole != [], chr(c)
|
||||
offset += 1
|
||||
break
|
||||
else:
|
||||
assert whole != [], chr(c)
|
||||
number = sign * int(bytes(whole))
|
||||
return offset, number
|
||||
# fraction
|
||||
while True:
|
||||
c = mem[offset]
|
||||
if c in string_digits:
|
||||
fraction.append(c)
|
||||
offset += 1
|
||||
else:
|
||||
assert fraction != [], chr(c)
|
||||
w = int(bytes(whole))
|
||||
f = int(bytes(fraction)) / (10 ** len(fraction))
|
||||
number = sign * (w + f)
|
||||
return offset, number
|
||||
|
||||
assert parse_number(b"1234;", 0)[1] == 1234
|
||||
assert abs(parse_number(b"1234.5678;", 0)[1] - 1234.5678) < 0.0001
|
||||
assert parse_number(b"-1234;", 0)[1] == -1234
|
||||
assert abs(parse_number(b"-1234.5678;", 0)[1] - -1234.5678) < 0.0001
|
||||
|
||||
whitespace = set([ord(' '), ord('\n')])
|
||||
|
||||
TOKEN_SEMICOLON = type("TOKEN_SEMICOLON", (), {})
|
||||
TOKEN_COMMA = type("TOKEN_COMMA", (), {})
|
||||
TOKEN_LBRACKET = type("TOKEN_LBRACKET", (), {})
|
||||
TOKEN_RBRACKET = type("TOKEN_RBRACKET", (), {})
|
||||
|
||||
identifier_start = set(map(ord, string.ascii_letters + "_"))
|
||||
identifier = identifier_start | string_digits
|
||||
|
||||
def parse_identifier(mem, offset):
|
||||
l = []
|
||||
while True:
|
||||
c = mem[offset]
|
||||
if c in identifier:
|
||||
l.append(c)
|
||||
offset += 1
|
||||
else:
|
||||
assert l != []
|
||||
return offset, bytes(l)
|
||||
|
||||
def parse_string(mem, offset):
|
||||
assert mem[offset] == ord('"')
|
||||
offset += 1
|
||||
start = offset
|
||||
while mem[offset] != ord('"'):
|
||||
assert mem[offset] != ord("\n")
|
||||
offset += 1
|
||||
assert mem[offset] == ord('"')
|
||||
s = bytes(mem[start:offset]).decode("utf-8")
|
||||
offset += 1
|
||||
return offset, s
|
||||
|
||||
def next_token(mem, offset):
|
||||
while True:
|
||||
if offset >= len(mem):
|
||||
return offset, None
|
||||
c = mem[offset]
|
||||
if c in whitespace:
|
||||
offset += 1
|
||||
else:
|
||||
break
|
||||
|
||||
if c in string_digits or c == ord('-'):
|
||||
return parse_number(mem, offset)
|
||||
elif c == ord(';'):
|
||||
return offset + 1, TOKEN_SEMICOLON
|
||||
elif c == ord(','):
|
||||
return offset + 1, TOKEN_COMMA
|
||||
elif c == ord('{'):
|
||||
return offset + 1, TOKEN_LBRACKET
|
||||
elif c == ord('}'):
|
||||
return offset + 1, TOKEN_RBRACKET
|
||||
elif c == ord('"'):
|
||||
return parse_string(mem, offset)
|
||||
elif c in identifier_start:
|
||||
return parse_identifier(mem, offset)
|
||||
else:
|
||||
assert False, chr(c)
|
||||
|
||||
def lex_all(mem, offset):
|
||||
offset = parse_magic(mem, offset)
|
||||
while True:
|
||||
offset, token = next_token(mem, offset)
|
||||
if token is None:
|
||||
return
|
||||
else:
|
||||
yield token
|
370
x/parse.py
Normal file
370
x/parse.py
Normal file
@ -0,0 +1,370 @@
|
||||
from pprint import pprint
|
||||
import sys
|
||||
|
||||
import lex
|
||||
import templates
|
||||
|
||||
with open(sys.argv[1], "rb") as f:
|
||||
buf = f.read()
|
||||
mem = memoryview(buf)
|
||||
|
||||
class TokenReader:
|
||||
def __init__(self, mem):
|
||||
self.tokens = list(lex.lex_all(mem, 0))
|
||||
self.ix = 0
|
||||
|
||||
def consume(self, o):
|
||||
assert self.tokens[self.ix] == o, (self.tokens[self.ix], o)
|
||||
self.ix += 1
|
||||
|
||||
def consume_type(self, t):
|
||||
assert type(self.tokens[self.ix]) == t, (t,
|
||||
self.tokens[self.ix],
|
||||
type(self.tokens[self.ix]))
|
||||
self.ix += 1
|
||||
return self.tokens[self.ix-1]
|
||||
|
||||
def match(self, o):
|
||||
if self.tokens[self.ix] == o:
|
||||
self.ix += 1
|
||||
return self.tokens[self.ix-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
def match_type(self, t):
|
||||
if type(self.tokens[self.ix]) == t:
|
||||
self.ix += 1
|
||||
return self.tokens[self.ix-1]
|
||||
else:
|
||||
return False
|
||||
|
||||
def peek(self):
|
||||
return self.tokens[self.ix]
|
||||
|
||||
def eof(self):
|
||||
return self.ix >= len(self.tokens)
|
||||
|
||||
def parse_int_raw(r):
|
||||
i = r.consume_type(int)
|
||||
return i
|
||||
|
||||
def parse_float_raw(r):
|
||||
i = r.consume_type(float)
|
||||
return i
|
||||
|
||||
def parse_int(r):
|
||||
i = r.consume_type(int)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
return i
|
||||
|
||||
def parse_float(r):
|
||||
f = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
return f
|
||||
|
||||
def parse_vector(r):
|
||||
x = parse_float(r)
|
||||
y = parse_float(r)
|
||||
z = parse_float(r)
|
||||
return templates.Vector(x, y, z)
|
||||
|
||||
def parse_string(r):
|
||||
s = r.consume_type(str)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
return s
|
||||
|
||||
def parse_color_rgba(r):
|
||||
red = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_COMMA)
|
||||
green = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_COMMA)
|
||||
blue = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_COMMA)
|
||||
alpha = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
return templates.ColorRGBA(
|
||||
red, green, blue, alpha
|
||||
)
|
||||
|
||||
def parse_color_rgb(r):
|
||||
red = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_COMMA)
|
||||
green = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_COMMA)
|
||||
blue = r.consume_type(float)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
return templates.ColorRGB(
|
||||
red, green, blue
|
||||
)
|
||||
|
||||
def parse_list(r, n, parse, *, delim=lex.TOKEN_COMMA):
|
||||
l = []
|
||||
for _ in range(n - 1):
|
||||
l.append(parse(r))
|
||||
if delim is not None:
|
||||
r.consume(delim)
|
||||
l.append(parse(r))
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
return l
|
||||
|
||||
def parse_matrix4x4(r):
|
||||
v = []
|
||||
for i in range(15):
|
||||
v.append(r.consume_type(float))
|
||||
r.consume(lex.TOKEN_COMMA)
|
||||
v.append(r.consume_type(float))
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
r.consume(lex.TOKEN_SEMICOLON)
|
||||
return templates.Matrix4x4(
|
||||
v
|
||||
)
|
||||
|
||||
def parse_header(r):
|
||||
r.consume(b"Header")
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
major = parse_int(r)
|
||||
minor = parse_int(r)
|
||||
flags = parse_int(r)
|
||||
r.consume(lex.TOKEN_RBRACKET)
|
||||
return templates.Header(
|
||||
major, minor, flags
|
||||
)
|
||||
|
||||
def parse_material(r):
|
||||
r.consume(b"Material")
|
||||
name = r.consume_type(bytes)
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
faceColor = parse_color_rgba(r)
|
||||
power = parse_float(r)
|
||||
specularColor = parse_color_rgb(r)
|
||||
emissiveColor = parse_color_rgb(r)
|
||||
|
||||
objects = []
|
||||
while not r.match(lex.TOKEN_RBRACKET):
|
||||
objects.append(parse_one_ref(r))
|
||||
|
||||
return name, templates.Material(
|
||||
faceColor,
|
||||
power,
|
||||
specularColor,
|
||||
emissiveColor,
|
||||
objects
|
||||
)
|
||||
|
||||
def parse_texture_filename(r):
|
||||
r.consume(b"TextureFilename")
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
filename = parse_string(r)
|
||||
r.consume(lex.TOKEN_RBRACKET)
|
||||
return templates.TextureFilename(
|
||||
filename
|
||||
)
|
||||
|
||||
def parse_frame(r):
|
||||
r.consume(b"Frame")
|
||||
name = r.consume_type(bytes)
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
objects = []
|
||||
while not r.match(lex.TOKEN_RBRACKET):
|
||||
objects.append(parse_one_ref(r))
|
||||
return name, templates.Frame(
|
||||
objects
|
||||
)
|
||||
|
||||
def parse_frame_transform_matrix(r):
|
||||
r.consume(b"FrameTransformMatrix")
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
frameMatrix = parse_matrix4x4(r)
|
||||
r.consume(lex.TOKEN_RBRACKET)
|
||||
return templates.FrameTransformMatrix(
|
||||
frameMatrix
|
||||
)
|
||||
|
||||
def parse_mesh_face(r):
|
||||
nFaceVertexIndices = parse_int(r)
|
||||
faceVertexIndices = parse_list(r, nFaceVertexIndices, parse_int_raw)
|
||||
|
||||
return templates.MeshFace(
|
||||
nFaceVertexIndices,
|
||||
faceVertexIndices,
|
||||
)
|
||||
|
||||
def parse_mesh(r):
|
||||
r.consume(b"Mesh")
|
||||
name = r.consume_type(bytes)
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
nVertices = parse_int(r)
|
||||
vertices = parse_list(r, nVertices, parse_vector)
|
||||
nFaces = parse_int(r)
|
||||
faces = parse_list(r, nFaces, parse_mesh_face)
|
||||
|
||||
objects = []
|
||||
while not r.match(lex.TOKEN_RBRACKET):
|
||||
objects.append(parse_one_ref(r))
|
||||
|
||||
return name, templates.Mesh(
|
||||
nVertices,
|
||||
vertices,
|
||||
nFaces,
|
||||
faces,
|
||||
objects
|
||||
)
|
||||
|
||||
def parse_mesh_material_list(r):
|
||||
r.consume(b"MeshMaterialList")
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
|
||||
nMaterials = parse_int(r)
|
||||
nFaceIndices = parse_int(r)
|
||||
faceIndices = parse_list(r, nFaceIndices, parse_int, delim=None)
|
||||
|
||||
objects = []
|
||||
while not r.match(lex.TOKEN_RBRACKET):
|
||||
objects.append(parse_one_ref(r, "Material"))
|
||||
|
||||
return templates.MeshMaterialList(
|
||||
nMaterials,
|
||||
nFaceIndices,
|
||||
faceIndices,
|
||||
objects
|
||||
)
|
||||
|
||||
def parse_mesh_normals(r):
|
||||
r.consume(b"MeshNormals")
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
nNormals = parse_int(r)
|
||||
normals = parse_list(r, nNormals, parse_vector)
|
||||
nFaceNormals = parse_int(r)
|
||||
faceNormals = parse_list(r, nFaceNormals, parse_mesh_face)
|
||||
r.consume(lex.TOKEN_RBRACKET)
|
||||
return templates.MeshNormals(
|
||||
nNormals,
|
||||
normals,
|
||||
nFaceNormals,
|
||||
faceNormals
|
||||
)
|
||||
|
||||
def parse_coords2d(r):
|
||||
u = parse_float(r)
|
||||
v = parse_float(r)
|
||||
return templates.Coords2D(u, v)
|
||||
|
||||
def parse_mesh_texture_coords(r):
|
||||
r.consume(b"MeshTextureCoords")
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
nTextureCoords = parse_int(r)
|
||||
textureCoords = parse_list(r, nTextureCoords, parse_coords2d)
|
||||
r.consume(lex.TOKEN_RBRACKET)
|
||||
return templates.MeshTextureCoords(
|
||||
nTextureCoords,
|
||||
textureCoords
|
||||
)
|
||||
|
||||
def parse_animation_set(r):
|
||||
r.consume(b"AnimationSet")
|
||||
name = r.consume_type(bytes)
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
objects = []
|
||||
while not r.match(lex.TOKEN_RBRACKET):
|
||||
objects.append(parse_one_ref(r, b"Animation"))
|
||||
|
||||
return name, templates.AnimationSet(
|
||||
objects
|
||||
)
|
||||
|
||||
def parse_animation(r):
|
||||
r.consume(b"Animation")
|
||||
name = r.consume_type(bytes)
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
objects = []
|
||||
while not r.match(lex.TOKEN_RBRACKET):
|
||||
objects.append(parse_one_ref(r, {b"AnimationKey", b"AnimationOptions"}))
|
||||
|
||||
return name, templates.Animation(
|
||||
objects
|
||||
)
|
||||
|
||||
def parse_float_keys(r):
|
||||
nValues = parse_int(r)
|
||||
values = parse_list(r, nValues, parse_float_raw)
|
||||
r.consume(lex.TOKEN_SEMICOLON) # FIXME: is this correct for nKeys>1?
|
||||
return templates.TimedFloatKeys(
|
||||
nValues,
|
||||
values,
|
||||
)
|
||||
|
||||
def parse_timed_float_keys(r):
|
||||
time = parse_int(r)
|
||||
tfkeys = parse_float_keys(r)
|
||||
return templates.TimedFloatKeys(
|
||||
time,
|
||||
tfkeys,
|
||||
)
|
||||
|
||||
def parse_animation_key(r):
|
||||
r.consume(b"AnimationKey")
|
||||
r.consume(lex.TOKEN_LBRACKET)
|
||||
keyType = parse_int(r)
|
||||
nKeys = parse_int(r)
|
||||
keys = parse_list(r, nKeys, parse_timed_float_keys)
|
||||
r.consume(lex.TOKEN_RBRACKET)
|
||||
return templates.AnimationKey(
|
||||
keyType,
|
||||
nKeys,
|
||||
keys,
|
||||
)
|
||||
|
||||
def parse_one(r, peek_token=None):
|
||||
token = r.peek()
|
||||
if peek_token != None:
|
||||
if type(peek_token) is set:
|
||||
assert token in peek_token, (token, peek_token)
|
||||
else:
|
||||
assert token == peek_token, (token, peek_token)
|
||||
if token == b"Header":
|
||||
return parse_header(r)
|
||||
elif token == b"Material":
|
||||
return parse_material(r)
|
||||
elif token == b"TextureFilename":
|
||||
return parse_texture_filename(r)
|
||||
elif token == b"Frame":
|
||||
return parse_frame(r)
|
||||
elif token == b"FrameTransformMatrix":
|
||||
return parse_frame_transform_matrix(r)
|
||||
elif token == b"Mesh":
|
||||
return parse_mesh(r)
|
||||
elif token == b"MeshMaterialList":
|
||||
return parse_mesh_material_list(r)
|
||||
elif token == b"MeshNormals":
|
||||
return parse_mesh_normals(r)
|
||||
elif token == b"MeshTextureCoords":
|
||||
return parse_mesh_texture_coords(r)
|
||||
elif token == b"AnimationSet":
|
||||
return parse_animation_set(r)
|
||||
elif token == b"Animation":
|
||||
return parse_animation(r)
|
||||
elif token == b"AnimationKey":
|
||||
return parse_animation_key(r)
|
||||
else:
|
||||
assert False, token
|
||||
|
||||
def parse_one_ref(r, peek_token=None):
|
||||
if r.match(lex.TOKEN_LBRACKET):
|
||||
name = r.consume_type(bytes)
|
||||
r.consume(lex.TOKEN_RBRACKET)
|
||||
return templates.Reference(
|
||||
name
|
||||
)
|
||||
else:
|
||||
token = r.peek()
|
||||
return parse_one(r, peek_token)
|
||||
|
||||
def parse_all(r):
|
||||
while not r.eof():
|
||||
yield parse_one_ref(r)
|
||||
|
||||
r = TokenReader(mem)
|
||||
for i in parse_all(r):
|
||||
pprint(i)
|
124
x/templates.py
Normal file
124
x/templates.py
Normal file
@ -0,0 +1,124 @@
|
||||
from typing import Any, Union
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class Header:
|
||||
major: int
|
||||
minor: int
|
||||
flags: int
|
||||
|
||||
@dataclass
|
||||
class ColorRGBA:
|
||||
r: float
|
||||
g: float
|
||||
b: float
|
||||
a: float
|
||||
|
||||
@dataclass
|
||||
class ColorRGB:
|
||||
r: float
|
||||
g: float
|
||||
b: float
|
||||
|
||||
@dataclass
|
||||
class Matrix4x4:
|
||||
v: list[float]
|
||||
|
||||
@dataclass
|
||||
class Material:
|
||||
faceColor: ColorRGBA
|
||||
power: float
|
||||
specularColor: ColorRGB
|
||||
emissiveColor: ColorRGB
|
||||
|
||||
objects: list[Any]
|
||||
|
||||
@dataclass
|
||||
class Frame:
|
||||
objects: list[Any]
|
||||
|
||||
@dataclass
|
||||
class FrameTransformMatrix:
|
||||
frameMatrix: Matrix4x4
|
||||
|
||||
@dataclass
|
||||
class Vector:
|
||||
x: float
|
||||
y: float
|
||||
z: float
|
||||
|
||||
@dataclass
|
||||
class MeshFace:
|
||||
nFaceVertexIndices: int
|
||||
faceVertexIndices: list[int]
|
||||
|
||||
@dataclass
|
||||
class Mesh:
|
||||
nVertices: int
|
||||
vertices: list[Vector]
|
||||
nFaces: int
|
||||
faces: list[MeshFace]
|
||||
|
||||
objects: list[Any]
|
||||
|
||||
@dataclass
|
||||
class MeshMaterialList:
|
||||
nMaterials: int
|
||||
nFaceIndices: int
|
||||
faceIndices: list[int]
|
||||
|
||||
objects: list[Material]
|
||||
|
||||
@dataclass
|
||||
class MeshNormals:
|
||||
nNormals: int
|
||||
normals: list[Vector]
|
||||
nFaceNormals: int
|
||||
faceNormals: list[MeshFace]
|
||||
|
||||
@dataclass
|
||||
class Coords2D:
|
||||
u: float
|
||||
v: float
|
||||
|
||||
@dataclass
|
||||
class MeshTextureCoords:
|
||||
nTextureCoords: int
|
||||
textureCoords: list[Coords2D]
|
||||
|
||||
@dataclass
|
||||
class TextureFilename:
|
||||
filename: str
|
||||
|
||||
@dataclass
|
||||
class Reference:
|
||||
name: bytes
|
||||
|
||||
@dataclass
|
||||
class FloatKeys:
|
||||
nValues: int
|
||||
values: list[float]
|
||||
|
||||
@dataclass
|
||||
class TimedFloatKeys:
|
||||
time: int
|
||||
tfkeys: FloatKeys
|
||||
|
||||
@dataclass
|
||||
class AnimationKey:
|
||||
keyType: int
|
||||
nKeys: int
|
||||
keys: list[TimedFloatKeys]
|
||||
|
||||
@dataclass
|
||||
class AnimationOptions:
|
||||
openClosed: int
|
||||
positionQuality: int
|
||||
|
||||
@dataclass
|
||||
class Animation:
|
||||
objects: list[Union[Reference, AnimationKey, AnimationOptions]]
|
||||
|
||||
@dataclass
|
||||
class AnimationSet:
|
||||
objects: list[Animation]
|
109
x/x.h
Normal file
109
x/x.h
Normal file
@ -0,0 +1,109 @@
|
||||
struct header {
|
||||
int tag;
|
||||
int major;
|
||||
int minor;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct color_rgba {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
float a;
|
||||
};
|
||||
|
||||
struct color_rgb {
|
||||
float r;
|
||||
float g;
|
||||
float b;
|
||||
};
|
||||
|
||||
struct material {
|
||||
int tag;
|
||||
x_color_rgba facecolor;
|
||||
float power;
|
||||
x_color_rgb specularcolor;
|
||||
x_color_rgb emissivecolor;
|
||||
void * objects[];
|
||||
};
|
||||
|
||||
struct frame {
|
||||
int tag;
|
||||
void * objects[];
|
||||
};
|
||||
|
||||
struct frame_transform_matrix {
|
||||
int tag;
|
||||
mat4x4 framematrix;
|
||||
};
|
||||
|
||||
struct mesh_face {
|
||||
int nfacevertexindices;
|
||||
int facevertexindices[];
|
||||
};
|
||||
|
||||
struct mesh {
|
||||
int nvertices;
|
||||
vec3 * vertices;
|
||||
int nfaces;
|
||||
mesh_face * faces;
|
||||
void * objects[];
|
||||
};
|
||||
|
||||
struct mesh_material_list {
|
||||
int n_materials;
|
||||
int n_face_indices;
|
||||
int * face_indices;
|
||||
void * objects[];
|
||||
};
|
||||
|
||||
struct mesh_normals {
|
||||
int tag;
|
||||
int n_normals;
|
||||
vec3 * normals;
|
||||
int n_face_normals;
|
||||
mesh_face * face_normals;
|
||||
};
|
||||
|
||||
struct mesh_texture_coords {
|
||||
int tag;
|
||||
int n_texture_coords;
|
||||
vec2 texture_coords[];
|
||||
};
|
||||
|
||||
struct texture_filename {
|
||||
const char * filename;
|
||||
};
|
||||
|
||||
struct float_keys {
|
||||
int nvalues;
|
||||
float values;
|
||||
};
|
||||
|
||||
struct timed_float_keys {
|
||||
int time;
|
||||
float_keys tfkeys;
|
||||
};
|
||||
|
||||
struct animation_key {
|
||||
int tag;
|
||||
int key_type;
|
||||
int n_keys;
|
||||
timed_float_keys keys[];
|
||||
};
|
||||
|
||||
struct animation_options {
|
||||
int tag;
|
||||
int open_closed;
|
||||
int position_quality;
|
||||
};
|
||||
|
||||
struct animation {
|
||||
int tag;
|
||||
void * objects[];
|
||||
};
|
||||
|
||||
struct animation_set {
|
||||
int tag;
|
||||
void * objects[];
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user