This commit is contained in:
Zack Buhman 2025-01-30 03:07:20 -06:00
commit 61e2fb2c28
9 changed files with 365 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
__pycache__/
*.pyc

42
generate.py Normal file
View File

@ -0,0 +1,42 @@
import io
def should_autonewline(line):
return (
"static_assert" not in line
and "extern" not in line
and (len(line.split()) < 2 or line.split()[1] != '=') # hacky; meh
)
def _render(out, lines, indent_length):
indent = " "
level = 0
namespace = 0
for l in lines:
if l and (l[0] == "}" or l[0] == ")"):
level -= indent_length
if level < 0:
assert namespace >= 0
namespace -= 1
level = 0
if len(l) == 0:
out.write("\n")
else:
out.write(indent * level + l + "\n")
if l and (l[-1] == "{" or l[-1] == "("):
if l.startswith("namespace"):
namespace += 1
else:
level += indent_length
if level == 0 and l and l[-1] == ";":
if should_autonewline(l):
out.write("\n")
return out
def renderer(indent_length=2):
out = io.StringIO()
def render(lines):
return _render(out, lines, indent_length)
return render, out

61
generate_cpp.py Normal file
View File

@ -0,0 +1,61 @@
from generate import renderer
from obj import parse_obj_file
import sys
def generate_vec3(vs):
for v in vs:
yield f"{{{v.x}f, {v.y}f, {v.z}f}},"
def generate_vec2(vs):
for v in vs:
yield f"{{{v.x}f, {v.y}f}},"
def generate_vec(name, type, func, vs):
yield f"vertex_{type} {name}_{type}[] = {{"
yield from func(vs)
yield "};"
def filter_n(l, n):
for a in l:
if len(a.ptn) == n:
yield a
def generate_ptn(ptn):
for p in ptn:
yield f"{{{p.position}, {p.texture}, {p.normal}}},"
def generate_faces(group_name, model_name, faces, type, n):
yield f"union {type} {group_name}_{model_name}_triangles = {{"
for face in filter_n(faces, n):
yield "{ .v = {"
yield from generate_ptn(face.ptn)
yield "}},"
yield "};"
def generate_object(group_name, object):
yield f"struct object {group_name}_{object.name} = {{"
yield f".triangle = &{group_name}_{object.name}_triangle[0],"
yield f".quadrilateral = &{group_name}_{object.name}_quadrilateral[0],"
triangle_count = sum(1 for _ in filter_n(object.faces, 3))
quadrilateral_count = sum(1 for _ in filter_n(object.faces, 4))
yield f".triangle_count = {triangle_count},"
yield f".quadrilateral_count = {quadrilateral_count},"
yield ".material = 0,"
yield "};"
def generate_obj_file(group_name, obj_file):
yield from generate_vec(group_name, "position", generate_vec3, obj_file.position)
yield from generate_vec(group_name, "texture", generate_vec2, obj_file.texture)
yield from generate_vec(group_name, "normal", generate_vec3, obj_file.normal)
for object in obj_file.objects:
assert all(len(face.ptn) in {3, 4} for face in object.faces), object.name
yield from generate_faces(group_name, object.name, object.faces, "triangle", 3)
yield from generate_faces(group_name, object.name, object.faces, "quadrilateral", 4)
yield from generate_object(group_name, object)
if __name__ == "__main__":
obj_file = parse_obj_file(sys.argv[1])
render, out = renderer(indent_length=4)
render(generate_obj_file(sys.argv[2], obj_file))
sys.stdout.write(out.getvalue())

57
generate_java.py Normal file
View File

@ -0,0 +1,57 @@
from generate import renderer
from obj import parse_obj_file
import sys
def generate_vec3(field, vs):
for i, v in enumerate(vs):
vec3_args = f"{v.x}f, {v.y}f, {v.z}f"
yield f"{field}[{i}] = new Vec3({vec3_args});"
def generate_vec2(field, vs):
for i, v in enumerate(vs):
vec2_args = f"{v.x}f, {v.y}f"
yield f"{field}[{i}] = new Vec2({vec2_args});"
def generate_face(length, i, j, face):
assert len(face.ptn) == length, (face.ptn, length)
for k, ptn in enumerate(face.ptn):
ptn_args = f"{ptn.position}, {ptn.texture}, {ptn.normal}"
yield f"objects[{i}].faces[{j}][{k}] = new FacePTN({ptn_args});"
def generate_model_objects(objects):
face_length = len(objects[0].faces[0].ptn)
for i, o in enumerate(objects):
yield f"objects[{i}] = new ModelObject();"
yield f"objects[{i}].faces = new FacePTN[{len(o.faces)}][{face_length}];"
for j, face in enumerate(objects[i].faces):
yield from generate_face(face_length, i, j, face)
def generate_model(name, obj_file):
yield "package model;"
yield f"public class {name} {{"
yield "public static Vec3[] position;"
yield "public static Vec3[] normal;"
yield "public static Vec2[] texture;"
yield "public static ModelObject[] objects;"
yield f"private {name}() {{"
yield "}"
yield "static {"
yield f"position = new Vec3[{len(obj_file.position)}];"
yield f"normal = new Vec3[{len(obj_file.normal)}];"
yield f"texture = new Vec2[{len(obj_file.texture)}];"
yield f"objects = new ModelObject[{len(obj_file.objects)}];"
yield from generate_vec3("position", obj_file.position)
yield from generate_vec3("normal", obj_file.normal)
yield from generate_vec2("texture", obj_file.texture)
yield from generate_model_objects(obj_file.objects)
yield "}"
yield "}"
if __name__ == "__main__":
obj_file = parse_obj_file(sys.argv[1])
render, out = renderer(indent_length=4)
render(generate_model(sys.argv[2], obj_file))
sys.stdout.write(out.getvalue())

13
java/model/FacePTN.java Normal file
View File

@ -0,0 +1,13 @@
package model;
public class FacePTN {
public int position;
public int texture;
public int normal;
FacePTN(int position, int texture, int normal) {
this.position = position;
this.texture = texture;
this.normal = normal;
}
}

View File

@ -0,0 +1,5 @@
package model;
public class ModelObject {
public FacePTN[][] faces;
}

11
java/model/Vec2.java Normal file
View File

@ -0,0 +1,11 @@
package model;
public class Vec2 {
public float x;
public float y;
public Vec2(float x, float y) {
this.x = x;
this.y = y;
}
}

13
java/model/Vec3.java Normal file
View File

@ -0,0 +1,13 @@
package model;
public class Vec3 {
public float x;
public float y;
public float z;
public Vec3(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
}

161
obj.py Normal file
View File

@ -0,0 +1,161 @@
from dataclasses import dataclass
import sys
@dataclass
class VertexPosition:
x: float
y: float
z: float
@dataclass
class VertexNormal:
x: float
y: float
z: float
@dataclass
class VertexTexture:
x: float
y: float
z: float
@dataclass
class ObjectEvent:
name: str
@dataclass
class FacePTN:
position: int
texture: int
normal: int
@dataclass
class Face:
ptn: list[FacePTN]
@dataclass
class Object:
name: str
faces: list[Face]
def __init__(self, name):
self.name = name
self.faces = []
@dataclass
class ObjFile:
position: list[VertexPosition]
normal: list[VertexNormal]
texture: list[VertexTexture]
objects: list[Object]
def __init__(self):
self.position = []
self.normal = []
self.texture = []
self.objects = []
def parse_float(s):
if '.' not in s:
return int(s, 10)
i, f = s.split('.')
f_digits = len(f)
i = int(i, 10)
f = int(f, 10)
return i + (f / 10 ** f_digits)
def parse_vertex(line, n, type):
assert len(line) == n
vs = [parse_float(line[i]) for i in range(n)]
if len(vs) < 3:
vs.append(0)
return type(*vs)
def parse_vertex_position(line):
return parse_vertex(line, 3, VertexPosition)
def parse_vertex_normal(line):
return parse_vertex(line, 3, VertexNormal)
def parse_vertex_texture(line):
return parse_vertex(line, 2, VertexTexture)
def parse_face_indices(indices):
assert "/" in indices
indices = indices.split("/")
assert len(indices) == 3, indices
def face_ix(s):
i = int(s, 10)
assert i >= 1
return i - 1
return FacePTN(*(face_ix(i) for i in indices))
def parse_face(line):
return Face([parse_face_indices(indices) for indices in line])
def parse_object_event(line):
assert len(line) == 1
name, = line
return ObjectEvent(name)
def parse_line(line):
t, *line = line.split(' ')
if t == '#':
return None
if t == 'usemtl':
return None
if t == 'mtllib':
return None
if t == 'o':
return parse_object_event(line)
if t == 'v':
return parse_vertex_position(line)
if t == 'vn':
return parse_vertex_normal(line)
if t == 'vt':
return parse_vertex_texture(line)
if t == 'f':
return parse_face(line)
if t == 's':
# smooth shading
return None
assert False, (t, line)
def parse_obj_lines(lines):
file = ObjFile()
object = Object(None)
for line in lines:
x = parse_line(line)
if x is None:
continue
elif type(x) is VertexPosition:
file.position.append(x)
elif type(x) is VertexNormal:
file.normal.append(x)
elif type(x) is VertexTexture:
file.texture.append(x)
elif type(x) is ObjectEvent:
if object.faces:
assert object.name != None
file.objects.append(object)
object = Object(x.name)
elif type(x) is Face:
object.faces.append(x)
else:
assert False, x
if object.faces:
assert object.name != None
file.objects.append(object)
return file
def parse_obj_file(filename):
with open(filename, "r") as f:
lines = f.read().strip().split("\n")
file = parse_obj_lines(lines)
return file
if __name__ == "__main__":
file = parse_obj_file(sys.argv[1])
from pprint import pprint
pprint(file)