hellen-one/bin/render_vrml/render_components.py

153 lines
5.1 KiB
Python

#!/usr/bin/env python
############################################################################################
# Hellen-One: A 3D-component VRML rendering server script.
# Python 3.5+ is required.
# Dependencies: ModernGL, Pillow, Pyrr, NumPy, PyVRML97, Gzip
# (c) andreika <prometheus.pcb@gmail.com>
############################################################################################
import os, sys
import numpy as np
from pyrr import Matrix33,Matrix44,Vector3,Vector4,aabb
from vrml.vrml97 import parser,basenodes
from vrml import *
import moderngl as ModernGL
from moderngl_mesh import Mesh
from PIL import Image
import gzip
if (len(sys.argv) < 3):
print ("Error! Please specify the VRML+image file names and DPI.")
sys.exit(1)
fileName = sys.argv[1]
outFileName = sys.argv[2]
dpi = float(sys.argv[3])
curLocation = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
# uses ModernGL (OpenGL-based renderer with HW acceleration)
def render(model, outFileName, dpi):
vertex_data = model.pack('vx vy vz cx cy cz nx ny nz')
# Context creation
ctx = ModernGL.create_standalone_context()
# Shaders
vertex_shader_source = open(os.path.join(curLocation, 'shader.vert')).read()
fragment_shader_source = open(os.path.join(curLocation, 'shader.frag')).read()
prog = ctx.program(vertex_shader = vertex_shader_source, fragment_shader = fragment_shader_source)
# Matrices and Uniforms
perspective = Matrix44.orthogonal_projection(model.aabb[0][0], model.aabb[1][0], model.aabb[0][1], model.aabb[1][1], 0.1, 1000.0) # left,right,top,bottom,near,far
lookat = Matrix44.look_at(
(0,0,300),
(0,0,0),
(0.0, 1.0, 0.0),
)
mvp = perspective * lookat
prog['LightDir'].value = (0.0, 0.0, -1.0)
prog['AmbientColor'].value = (1.0, 1.0, 1.0, 0.25)
prog['Mvp'].write(mvp.astype('f4').tobytes())
# Vertex Buffer and Vertex Array
vbo = ctx.buffer(vertex_data)
vao = ctx.simple_vertex_array(prog, vbo, * ['in_vert', 'in_color', 'in_norm'])
(width, height) = ((model.aabb[1][0:2] - model.aabb[0][0:2]) * dpi / 25.4 + 0.5).astype(int)
print ("* Image size = " + str(width) + "x" + str(height))
# Framebuffer
fbo = ctx.framebuffer(ctx.renderbuffer((width, height)), ctx.depth_renderbuffer((width, height)),)
# Rendering
fbo.use()
ctx.enable(ModernGL.DEPTH_TEST)
ctx.clear(0.0, 0.0, 0.0, 0.0)
vao.render()
# Save the image using Pillow
print ("* Saving to " + str(outFileName) + "...")
data = fbo.read(components=4, alignment=1)
img = Image.frombytes('RGBA', fbo.size, data, 'raw', 'RGBA', 0, -1)
img.save(outFileName)
def addFaces(model, faces, mat, tm):
offs = len(model.vert)
# create an inverse transform matrix for normals
tnm = Matrix33(tm)
tnm = tnm.inverse.T
# add vertices
for i in range(0, len(faces.coord.point)):
# position
x = float(faces.coord.point[i][0])
y = float(faces.coord.point[i][1])
z = float(faces.coord.point[i][2])
v = Vector4([x, y, z, 1.0])
tv = tm * v
# get normal (if present)
try:
(nx,ny,nz) = (faces.normal.vector[i][0:3])
nv = Vector3([nx, ny, nz])
except AttributeError:
nv = Vector3([0,0,-1]) # default normal
tnv = tnm * nv
tnv.normalize()
model.vert.append((tv.x, tv.y, tv.z))
model.norm.append((tnv.x, tnv.y, tnv.z))
model.color.append((mat.diffuseColor[0], mat.diffuseColor[1], mat.diffuseColor[2]))
model.aabb = aabb.add_points(model.aabb, [tv.xyz])
# add indices
numFaces = int(len(faces.coordIndex) / 4)
for i in range(0, numFaces):
for ci in range(0, 3):
idx = faces.coordIndex[i * 4 + ci]
# indices are 1-based
ind = int(idx + offs) + 1
model.face.append((ind, ind, ind))
def processChildren(model, ch, tm):
# iterate through all nodes
for i,node in enumerate(ch):
if (type(node) is basenodes.Group):
processChildren(model, node.children, tm)
continue
if (type(node) is basenodes.Transform):
transform = Matrix44(node.localMatrices().data[0].tolist()) if (node.localMatrices().data[0] is not None) else Matrix44.identity()
# apply transform
combinedTransform = tm * transform
processChildren(model, node.children, combinedTransform)
continue
if (type(node) is basenodes.Shape and type(node.geometry) is basenodes.IndexedFaceSet):
addFaces(model, node.geometry, node.appearance.material, tm)
############################################################################################
print ("* Reading " + fileName + "...")
if fileName.endswith('.gz'):
print ("* Decompressing GZip...")
data = gzip.open(fileName, "rt").read()
else:
data = open(fileName).read()
print ("* Parsing the VRML data...")
parser = vrml97.parser.buildParser()
success, tags, next = parser.parse(data)
if (not success):
print("VRML Parsing error: parsed %s characters of %s"%(next, len(data)))
sys.exit(1)
print ("* Processing the data...")
model = Mesh([], [], [], [])
processChildren(model, tags[1].children, Matrix44.identity())
print ("* Num.Vertices = " + str(len(model.vert)) + " Num.Indices = " + str(len(model.face)))
print ("* AABB = " + str(model.aabb))
print ("* Rendering...")
render(model, outFileName, dpi)
print ("* Done!")