Merge pull request #12 from DarkflameUniverse/property-viewer-v2

Property viewer v2
This commit is contained in:
Aaron Kimbrell 2022-02-11 17:51:40 -06:00 committed by GitHub
commit 87384c1b98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 146 additions and 59 deletions

View File

@ -12,7 +12,7 @@ from flask_wtf.csrf import CSRFProtect
from flask_apscheduler import APScheduler
from app.luclient import query_cdclient, register_luclient_jinja_helpers
from app.commands import init_db, init_accounts, load_property
from app.commands import init_db, init_accounts, load_property, gen_image_cache, gen_model_cache
from app.models import Account, AccountInvitation
# Instantiate Flask extensions
@ -65,6 +65,8 @@ def create_app():
app.cli.add_command(init_db)
app.cli.add_command(init_accounts)
app.cli.add_command(load_property)
app.cli.add_command(gen_image_cache)
app.cli.add_command(gen_model_cache)
register_settings(app)
register_extensions(app)

View File

@ -7,6 +7,12 @@ from app import db
from app.models import Account, PlayKey, CharacterInfo, Property, PropertyContent, UGC
import pathlib
import zlib
import os
from wand import image
from wand.exceptions import BlobError as BE
import app.pylddlib as ldd
from multiprocessing import Pool
from functools import partial
@click.command("init_db")
@click.argument('drop_tables', nargs=1)
@ -89,6 +95,54 @@ def load_property(zone, player):
)
new_prop_content.save()
@click.command("gen_image_cache")
def gen_image_cache():
luclient = pathlib.Path('app/luclient/res')
files = [path for path in luclient.rglob("*.dds") if path.is_file()]
for file in files:
cache = get_cache_file(file).with_suffix(".png")
if not cache.exists():
try:
print(f"Convert {file.as_posix()} to {cache}")
cache.parent.mkdir(parents=True, exist_ok=True)
with image.Image(filename=str(file.as_posix())) as img:
img.compression = "no"
img.save(filename=str(cache.as_posix()))
except BE:
return print(f"Error on {file}")
@click.command("gen_model_cache")
def gen_model_cache():
luclient = pathlib.Path('app/luclient/res')
files = [path for path in luclient.rglob("*.lxfml") if path.is_file()]
pool = Pool(processes=4)
pool.map(partial(convert_lxfml_to_obj, lod=0), files)
pool.map(partial(convert_lxfml_to_obj, lod=1), files)
pool.map(partial(convert_lxfml_to_obj, lod=2), files)
def convert_lxfml_to_obj(file, lod):
mtl = get_cache_file(file).with_suffix(f".lod{lod}.mtl")
if not mtl.exists():
mtl.parent.mkdir(parents=True, exist_ok=True)
print(f"Convert LXFML {file.as_posix()} to obj and mtl @ {mtl}")
try:
ldd.main(str(file.as_posix()), str(mtl.with_suffix("").as_posix()), lod) # convert to OBJ
except Exception as e:
print(f"ERROR on {file}:\n {e}")
else:
# print(f"Already Exists: {file} with LOD {lod}")
return
def get_cache_file(path):
# convert to list so that we can change elements
parts = list(path.parts)
# replace part that matches src with dst
parts[parts.index("luclient")] = "cache"
del parts[parts.index("res")]
return pathlib.Path(*parts)
def find_or_create_account(name, email, password, gm_level=9):
""" Find existing account or create new account """
@ -119,5 +173,3 @@ def find_or_create_account(name, email, password, gm_level=9):
db.session.add(play_key)
db.session.commit()
return # account

View File

@ -23,6 +23,7 @@ import zlib
import xmltodict
import os
import app.pylddlib as ldd
import pathlib
property_blueprint = Blueprint('properties', __name__)
@ -206,16 +207,16 @@ def get(status="all"):
return data
@property_blueprint.route('/view_model/<id>', methods=['GET'])
@property_blueprint.route('/view_model/<id>/<lod>', methods=['GET'])
@login_required
def view_model(id):
def view_model(id, lod):
property_content_data = PropertyContent.query.filter(PropertyContent.id==id).all()
# TODO: Restrict somehow
formatted_data = [
{
"obj": url_for('properties.get_model', id=property_content_data[0].id, file_format='obj'),
"mtl": url_for('properties.get_model', id=property_content_data[0].id, file_format='mtl'),
"obj": url_for('properties.get_model', id=property_content_data[0].id, file_format='obj', lod=lod),
"mtl": url_for('properties.get_model', id=property_content_data[0].id, file_format='mtl', lod=lod),
"lot": property_content_data[0].lot,
"id": property_content_data[0].id,
"pos": [{
@ -232,7 +233,8 @@ def view_model(id):
return render_template(
'ldd/ldd.html.j2',
content=formatted_data
content=formatted_data,
lod=lod
)
property_center = {
@ -245,9 +247,9 @@ property_center = {
}
@property_blueprint.route('/view_models/<id>', methods=['GET'])
@property_blueprint.route('/view_models/<id>/<lod>', methods=['GET'])
@login_required
def view_models(id):
def view_models(id, lod):
property_content_data = PropertyContent.query.filter(
PropertyContent.property_id==id
).order_by(PropertyContent.lot).all()
@ -273,8 +275,8 @@ def view_models(id):
# add new lot
consolidated_list.append(
{
"obj": url_for('properties.get_model', id=property_content_data[item].id, file_format='obj'),
"mtl": url_for('properties.get_model', id=property_content_data[item].id, file_format='mtl'),
"obj": url_for('properties.get_model', id=property_content_data[item].id, file_format='obj', lod=lod),
"mtl": url_for('properties.get_model', id=property_content_data[item].id, file_format='mtl', lod=lod),
"lot": property_content_data[item].lot,
"id": property_content_data[item].id,
"pos": [{
@ -293,18 +295,20 @@ def view_models(id):
'ldd/ldd.html.j2',
property_data=property_data,
content=consolidated_list,
center=property_center[property_data.zone_id]
center=property_center[property_data.zone_id],
lod=lod
)
@property_blueprint.route('/get_model/<id>/<file_format>', methods=['GET'])
@property_blueprint.route('/get_model/<id>/<file_format>/<lod>', methods=['GET'])
@login_required
def get_model(id, file_format):
def get_model(id, file_format, lod):
content = PropertyContent.query.filter(PropertyContent.id==id).first()
if not(0 <= int(lod) <= 2):
abort(404)
if content.lot == 14: # ugc model
response = ugc(content)[0]
else: # prebuild model
response = prebuilt(content, file_format)[0]
response = prebuilt(content, file_format, lod)[0]
response.headers.set('Content-Type', 'text/xml')
return response
@ -337,7 +341,7 @@ def ugc(content):
return response, ugc_data.filename
def prebuilt(content, file_format):
def prebuilt(content, file_format, lod):
# translate LOT to component id
# we need to get a type of 2 because reasons
render_component_id = query_cdclient(
@ -357,7 +361,7 @@ def prebuilt(content, file_format):
return f"No filename for LOT {content.lot}"
if file_format == "lxfml":
lxfml = f'app/luclient/res/BrickModels/{filename.split(".")[0]}.lxfml'
lxfml = pathilob.Path(f'app/luclient/res/BrickModels/{filename.split(".")[0]}.lxfml')
with open(lxfml, 'r') as file:
lxfml_data = file.read()
# print(lxfml_data)
@ -365,21 +369,15 @@ def prebuilt(content, file_format):
elif file_format in ["obj", "mtl"]:
cache = f"app/cache/{filename}.{file_format}"
cache = f"app/cache/BrickModels/{filename}.lod{lod}.{file_format}"
if os.path.exists(cache):
with open(cache, 'r') as file:
cache_data = file.read()
response = make_response(cache_data)
else:
if not os.path.exists(cache):
lxfml = f'app/luclient/res/BrickModels/{filename.split(".")[0]}.lxfml'
ldd.main(lxfml, cache.split('.')[0]) # convert to OBJ
if os.path.exists(cache):
with open(cache, 'r') as file:
cache_data = file.read()
response = make_response(cache_data)
with open(cache, 'r') as file:
cache_data = file.read()
response = make_response(cache_data)
else:
raise(Exception("INVALID FILE FORMAT"))

View File

@ -30,7 +30,7 @@ if sys.version_info < (3, 0):
sys.setdefaultencoding('utf-8')
PRIMITIVEPATH = '/Primitives/'
GEOMETRIEPATH = PRIMITIVEPATH + 'LOD0/'
GEOMETRIEPATH = PRIMITIVEPATH
DECORATIONPATH = '/Decorations/'
MATERIALNAMESPATH = '/MaterialNames/'
@ -238,10 +238,10 @@ class Scene:
for childnode in node.childNodes:
if childnode.nodeName == 'BrickSet':
self.Version = str(childnode.getAttribute('version'))
elif node.nodeName == 'Cameras':
for childnode in node.childNodes:
if childnode.nodeName == 'Camera':
self.Scenecamera.append(SceneCamera(node=childnode))
# elif node.nodeName == 'Cameras':
# for childnode in node.childNodes:
# if childnode.nodeName == 'Camera':
# self.Scenecamera.append(SceneCamera(node=childnode))
elif node.nodeName == 'Bricks':
for childnode in node.childNodes:
if childnode.nodeName == 'Brick':
@ -720,8 +720,10 @@ class Converter:
start_time = time.time()
out = open(filename + ".obj.tmp", "w+")
out.truncate(0)
out.write("mtllib " + filename + ".mtl" + '\n\n')
outtext = open(filename + ".mtl.tmp", "w+")
outtext.truncate(0)
total = len(self.scene.Bricks)
current = 0
@ -823,13 +825,13 @@ class Converter:
sys.stdout.write('%s\r' % (' '))
# print("--- %s seconds ---" % (time.time() - start_time))
def setDBFolderVars(dbfolderlocation):
def setDBFolderVars(dbfolderlocation, lod):
global PRIMITIVEPATH
global GEOMETRIEPATH
global DECORATIONPATH
global MATERIALNAMESPATH
PRIMITIVEPATH = os.path.join(dbfolderlocation, 'Primitives', '')
GEOMETRIEPATH = os.path.join(dbfolderlocation, 'brickprimitives', 'lod0', '')
GEOMETRIEPATH = os.path.join(dbfolderlocation, 'brickprimitives', f'lod{lod}', '')
DECORATIONPATH = os.path.join(dbfolderlocation, 'Decorations', '')
MATERIALNAMESPATH = os.path.join(dbfolderlocation, 'MaterialNames', '')
# print(MATERIALNAMESPATH)
@ -880,7 +882,7 @@ def progress(count, total, status='', suffix = ''):
sys.stdout.write('Progress: [%s] %s%s %s %s\r' % (bar, percents, '%', suffix, status))
sys.stdout.flush()
def main(lxf_filename, obj_filename):
def main(lxf_filename, obj_filename, lod="2"):
# print("- - - pylddlib - - -")
# print(" _ ")
# print(" [_]")
@ -890,10 +892,11 @@ def main(lxf_filename, obj_filename):
# print(" [=|=]")
# print("")
# print("- - - - - - - - - - - -")
global GEOMETRIEPATH
GEOMETRIEPATH = GEOMETRIEPATH + f"LOD{lod}/"
converter = Converter()
# print("Found DB folder. Will use this instead of db.lif!")
setDBFolderVars(dbfolderlocation = "app/luclient/res/")
setDBFolderVars(dbfolderlocation = "app/luclient/res/", lod=lod)
converter.LoadDBFolder(dbfolderlocation = "app/luclient/res/")
converter.LoadScene(filename=lxf_filename)
converter.Export(filename=obj_filename)

View File

@ -467,16 +467,16 @@
constructor(designID, database){
this.designID = designID
this.Parts = []
this.studsFields2D = []
let GeometryLocation = `${designID}.g`
let lod = {{ lod }}
let GeometryLocation = `lod${lod}/${designID}.g`
let PrimitiveLocation = `${designID}.xml`
let GeometryCount = 0
while (GeometryLocation in database.filelist) {
this.Parts[GeometryCount] = new GeometryReader(database.filelist[GeometryLocation].read())
GeometryCount = GeometryCount + 1
GeometryLocation = `${designID}.g${GeometryCount}`
GeometryLocation = `lod${lod}/${designID}.g${GeometryCount}`
}
let primitive = new Primitive(database.filelist[PrimitiveLocation].read())
this.Partname = primitive.Designname
@ -1019,7 +1019,7 @@
return self.filelist[filename];
}
parse(dburl) {
parse(dburl, folder="") {
let self = this;
let xhr = new XMLHttpRequest();
xhr.open('GET', dburl, false);
@ -1038,10 +1038,16 @@
let obj = data[i];
if (obj.type == 'directory'){
// parse subdirs
self.parse(dburl + obj.name + '/')
self.parse(dburl + obj.name + '/', obj.name)
}
else if (obj.type == 'file'){
self.filelist[obj.name] = new DBURLFile(dburl + obj.name, obj.name)
if (folder.includes("lod")){
self.filelist[`${folder}/${obj.name}`] = new DBURLFile(dburl + obj.name, obj.name)
} else {
self.filelist[obj.name] = new DBURLFile(dburl + obj.name, obj.name)
}
}
else {
console.log('Strange object parsed: ' + obj.type)
@ -1063,7 +1069,7 @@
let lxfml_file_list = [
{% for model in content %}
{% if model.lot == 14 %}
"{{url_for('properties.get_model', id=model.id, file_format='lxfml')}}"{{ ", " if not loop.last else "" }}
"{{url_for('properties.get_model', id=model.id, file_format='lxfml', lod=lod)}}"{{ ", " if not loop.last else "" }}
{% endif %}
{% endfor %}
]

View File

@ -81,11 +81,24 @@
</div>
{% else %}
<br/>
<h5 class="text-center">Render Quality</h5>
<div class="row">
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.view_models', id=property.id) }}'>
Render Property
href='{{ url_for('properties.view_models', id=property.id, lod=0) }}'>
High (0)
</a>
</div>
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.view_models', id=property.id, lod=1) }}'>
Med (1)
</a>
</div>
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.view_models', id=property.id, lod=2) }}'>
Low (2)
</a>
</div>
</div>

View File

@ -44,21 +44,12 @@
<div class="row">
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.get_model', id=item.id, file_format="lxfml") }}'>
href='{{ url_for('properties.get_model', id=item.id, file_format="lxfml", lod=0) }}'>
View Model XML
</a>
</div>
</div>
<br/>
<div class="row">
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.view_model', id=item.id) }}'>
Render Model
</a>
</div>
</div>
<br/>
<div class="row">
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
@ -67,5 +58,27 @@
</a>
</div>
</div>
<br/>
<h5 class="text-center">Render Quality</h5>
<div class="row">
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.view_model', id=item.id, lod=0) }}'>
High (0)
</a>
</div>
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.view_model', id=item.id, lod=1) }}'>
Med (1)
</a>
</div>
<div class="col text-center">
<a role="button" class="btn btn-primary btn-block"
href='{{ url_for('properties.view_model', id=item.id, lod=2) }}'>
Low (2)
</a>
</div>
</div>
</div>
</div>