Skip to content

Commit

Permalink
Improve material editor
Browse files Browse the repository at this point in the history
  • Loading branch information
tobspr committed Nov 23, 2016
1 parent 205fe00 commit da5410a
Show file tree
Hide file tree
Showing 5 changed files with 476 additions and 223 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ toolkit/bake_gi/resources/*.bam
toolkit/bake_gi/resources/*.blend
toolkit/bake_gi/scene

.vscode
2 changes: 0 additions & 2 deletions rpcore/render_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -668,8 +668,6 @@ def export_materials(self, pth):
material.emission.w, # arbitrary 1
) + "\n")

self.debug("Wrote materials to", pth)

def update_serialized_material(self, data):
""" Internal method to update a material from a given serialized material """
name = data[0]
Expand Down
250 changes: 158 additions & 92 deletions toolkit/material_editor/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
import os
import sys
import time
import math
import tempfile
import colorsys
from threading import Thread
from functools import partial

Expand All @@ -41,7 +43,7 @@
sys.path.insert(0, "../../")

from rplibs.six import iteritems # noqa
from rplibs.pyqt_imports import * #noqa
from rplibs.pyqt_imports import * # noqa

from ui.main_window_generated import Ui_MainWindow # noqa
from rpcore.util.network_communication import NetworkCommunication # noqa
Expand All @@ -50,7 +52,9 @@
# ONLY for debugging the viewer.
ALLOW_OUTDATED_MATERIALS = False


class MaterialData:

def __init__(self):
self.name = ""
self.shading_model = 0
Expand All @@ -64,6 +68,7 @@ def __init__(self):
self.basecolor_g = 0.6
self.basecolor_b = 0.6


class MaterialEditor(QMainWindow, Ui_MainWindow):

""" Interface to change the plugin settings """
Expand All @@ -81,47 +86,115 @@ def __init__(self):
QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)

self.materials = []
self.in_update = False
self.materials = []
self.material = MaterialData()

self.setupUi(self)
self.init_shading_models()
self.init_bindings()
self.update_material_list()
self.on_material_selected()

self.material = MaterialData()

qt_connect(self.cb_metallic, "stateChanged", self.set_metallic)
qt_connect(self.cb_shading_model, "currentIndexChanged", self.set_shading_model)
qt_connect(self.cb_material, "currentIndexChanged", self.set_material)
def init_bindings(self):
qt_connect(self.cb_shading_model, "currentIndexChanged", self.read_from_ui)
qt_connect(self.cb_material, "currentIndexChanged", self.on_material_selected)
qt_connect(self.cb_metallic, "stateChanged", self.read_from_ui)

self.sliders = [
(self.basecolor_r, self.lbl_basecolor_r, 0.0, 1.0, "basecolor_r"),
(self.basecolor_g, self.lbl_basecolor_g, 0.0, 1.0, "basecolor_g"),
(self.basecolor_b, self.lbl_basecolor_b, 0.0, 1.0, "basecolor_b"),
(self.slider_roughness, self.lbl_roughness, 0.0, 1.0, "roughness"),
(self.slider_specular, self.lbl_specular, 1.0, 2.51, "specular"),
(self.slider_normal, self.lbl_normal, 0.0, 1.0, "normal_strength"),
(self.slider_param1, self.lbl_param1, 0.0, 1.0, "shading_model_param1"),
]

for slider, lbl, start, end, prop in self.sliders:
qt_connect(slider, "valueChanged", self.update_sliders)
qt_connect(slider, "valueChanged", self.read_from_ui)

qt_connect(self.basecolor_1, "valueChanged", self.read_from_ui)
qt_connect(self.basecolor_2, "valueChanged", self.read_from_ui)
qt_connect(self.basecolor_3, "valueChanged", self.read_from_ui)

for cb in (self.cb_rgb, self.cb_srgb, self.cb_hsv):
qt_connect(cb, "toggled", self.write_to_ui)

def update_ui(self):
# Basecolor
labels = "R", "G", "B"
if self.cb_hsv.isChecked():
labels = "H", "S", "V"
self.lbl_basecolor1.setText(labels[0])
self.lbl_basecolor2.setText(labels[1])
self.lbl_basecolor3.setText(labels[2])

a, b, c = (self.basecolor_1.value() / 100.0,
self.basecolor_2.value() / 100.0,
self.basecolor_3.value() / 100.0)
rgb = self.tuple_to_basecolor(a, b, c)
self.lbl_basecolor_val1.setText("{:0.2f}".format(a))
self.lbl_basecolor_val2.setText("{:0.2f}".format(b))
self.lbl_basecolor_val3.setText("{:0.2f}".format(c))
self.lbl_color_preview.setStyleSheet("background: rgb({}, {}, {});".format(
int(rgb[0] * 255), int(rgb[1] * 255), int(rgb[2] * 255)))

# Shading model
self._update_shading_model()

def read_from_ui(self):
if self.in_update:
return

# Rest of sliders
for slider, lbl, start, end, prop in self.sliders:
val = (slider.value() / 100.0) * (end - start) + start
lbl.setText("{:0.2f}".format(val))
setattr(self.material, prop, val)

self.update_material_list()
self.set_material()
# Basecolor
rgb = self._get_ui_basecolor_rgb()
self.material.basecolor_r = rgb[0]
self.material.basecolor_g = rgb[1]
self.material.basecolor_b = rgb[2]

# Metallic
self.material.metallic = self.cb_metallic.isChecked()

# Shading model
self.material.shading_model = self.cb_shading_model.currentIndex()

self.update_ui()
self.send_update()

def write_to_ui(self):
self.in_update = True

def update_sliders(self):
if not self.in_update:
self.send_update()
# Basecolor
values = self.basecolor_to_tuple(self.material)
self.basecolor_1.setValue(values[0] * 100.0)
self.basecolor_2.setValue(values[1] * 100.0)
self.basecolor_3.setValue(values[2] * 100.0)

for slider, lbl, start, end, prop in self.sliders:
val = (slider.value() / 100.0) * (end - start) + start
lbl.setText("{:0.2f}".format(val))
setattr(self.material, prop, val)
# Shading model
self.cb_shading_model.setCurrentIndex(self.material.shading_model)

self.lbl_color_preview.setStyleSheet("background: rgb({}, {}, {});".format(
int(self.material.basecolor_r * 255),
int(self.material.basecolor_g * 255),
int(self.material.basecolor_b * 255)))
# Metallics
self.cb_metallic.setChecked(self.material.metallic)
self.slider_specular.setEnabled(not self.material.metallic)

# Rest of sliders
for slider, lbl, start, end, prop in self.sliders:
val = getattr(self.material, prop)
slider.setValue((val - start) / (end - start) * 100.0)

self.in_update = False
self.update_ui()

def _get_ui_basecolor_rgb(self):
""" Extracts the RGB color which is currently edited in the UI """
a, b, c = (self.basecolor_1.value() / 100.0,
self.basecolor_2.value() / 100.0,
self.basecolor_3.value() / 100.0)
return self.tuple_to_basecolor(a, b, c)

def update_material_list(self):
temp_path = os.path.join(tempfile.gettempdir(), "rp_materials.data")
Expand All @@ -136,89 +209,86 @@ def update_material_list(self):
while not os.path.isfile(temp_path) and time.time() - start_time < 5.0:
time.sleep(0.5)
if not os.path.isfile(temp_path):
QMessageBox.critical(self, "Error", "Render Pipeline not responding! Make sure a render pipeline application is running, and try again later.")
QMessageBox.critical(
self, "Error", "Render Pipeline not responding! Make sure a render pipeline application is running, and try again later.")
sys.exit(-1)
if not ALLOW_OUTDATED_MATERIALS:
time.sleep(0.5)
self._load_material_list(temp_path)

def _load_material_list(self, pth):
self.in_update = True
self.materials = []
self.cb_material.clear()
with open(pth) as handle:
for line in handle.readlines():
parts = line.strip().split(" ")
material = MaterialData()
material.name = parts[0]
material.basecolor_r = float(parts[1])
material.basecolor_g = float(parts[2])
material.basecolor_b = float(parts[3])
material.roughness = float(parts[4])
material.specular = float(parts[5])
material.metallic = float(parts[6]) > 0.5
material.shading_model = int(float(parts[7]))
material.normal_strength = float(parts[8])
material.shading_model_param1 = float(parts[9])
material.shading_model_param2 = float(parts[10])
material = self._read_in_material(parts)
self.materials.append(material)
print("Materials[] =", material.name)
self.cb_material.addItem(material.name)
self.in_update = False
print("Loaded material list")

def set_material(self):
if self.in_update:
return
def _read_in_material(self, parts):
material = MaterialData()
material.name = parts[0]
material.basecolor_r = float(parts[1])
material.basecolor_g = float(parts[2])
material.basecolor_b = float(parts[3])
material.roughness = float(parts[4])
material.specular = float(parts[5])
material.metallic = float(parts[6]) > 0.5
material.shading_model = int(float(parts[7]))
material.normal_strength = float(parts[8])
material.shading_model_param1 = float(parts[9])
material.shading_model_param2 = float(parts[10])
return material

def basecolor_to_tuple(self, mat):
def to_srgb(v): return math.pow(v, 1.0 / 2.2)
if self.cb_rgb.isChecked():
return mat.basecolor_r, mat.basecolor_g, mat.basecolor_b
elif self.cb_srgb.isChecked():
return to_srgb(mat.basecolor_r), to_srgb(mat.basecolor_g), to_srgb(mat.basecolor_b)
elif self.cb_hsv.isChecked():
return colorsys.rgb_to_hsv(mat.basecolor_r, mat.basecolor_g, mat.basecolor_b)
else:
assert False

def tuple_to_basecolor(self, a, b, c):
def from_srgb(v): return math.pow(v, 2.2)
if self.cb_rgb.isChecked():
return a, b, c
elif self.cb_srgb.isChecked():
return from_srgb(a), from_srgb(b), from_srgb(c)
elif self.cb_hsv.isChecked():
return colorsys.hsv_to_rgb(a, b, c)
else:
assert False

def on_material_selected(self):
index = self.cb_material.currentIndex()
if index >= len(self.materials):
if index < 0 or index >= len(self.materials):
print("Invalid material with index", index, "only have", len(self.materials), "materials")
return
self.in_update = True
material = self.materials[index]
print("Loaded material", material.name)
self.cb_shading_model.setCurrentIndex(material.shading_model)
self.cb_metallic.setChecked(material.metallic)
self.slider_roughness.setValue(material.roughness * 100.0)
self.slider_specular.setValue(material.specular / 2.51 * 100.0)
self.slider_normal.setValue(material.normal_strength * 100.0)
self.basecolor_r.setValue(material.basecolor_r * 100)
self.basecolor_g.setValue(material.basecolor_g * 100)
self.basecolor_b.setValue(material.basecolor_b * 100)
self.slider_param1.setValue(material.shading_model_param1 * 100)
self.material = material
self.in_update = False
self.update_sliders()
self.set_metallic()
self.set_shading_model()

def set_metallic(self):
if not self.in_update:
self.material.metallic = self.cb_metallic.isChecked()
self.slider_specular.setEnabled(not self.material.metallic)
self.send_update()
self.material = self.materials[index]
print("Loaded material", self.material.name)
self.write_to_ui()

def send_update(self):
if not self.in_update:
serialized = ("{} " * 11).format(
self.material.name,
self.material.basecolor_r,
self.material.basecolor_g,
self.material.basecolor_b,
self.material.roughness,
self.material.specular,
1.0 if self.material.metallic else 0.0,
self.material.shading_model,
self.material.normal_strength,
self.material.shading_model_param1,
self.material.shading_model_param2,
)
NetworkCommunication.send_async(NetworkCommunication.MATERIAL_PORT, "update_material " + serialized)

def set_shading_model(self):
if self.in_update:
return

serialized = ("{} " * 11).format(
self.material.name,
self.material.basecolor_r,
self.material.basecolor_g,
self.material.basecolor_b,
self.material.roughness,
self.material.specular,
1.0 if self.material.metallic else 0.0,
self.material.shading_model,
self.material.normal_strength,
self.material.shading_model_param1,
self.material.shading_model_param2,
)
NetworkCommunication.send_async(NetworkCommunication.MATERIAL_PORT, "update_material " + serialized)

def _update_shading_model(self):
name, val, optional_param = self.SHADING_MODELS[self.cb_shading_model.currentIndex()]
if optional_param is None:
self.slider_param1.setEnabled(False)
Expand All @@ -245,16 +315,12 @@ def set_shading_model(self):
self.cb_metallic.hide()
elif name == "Foliage":
self.cb_metallic.hide()

self.material.shading_model = val
self.send_update()

def init_shading_models(self):
self.cb_shading_model.clear()
for name, val, optional_param in self.SHADING_MODELS:
self.cb_shading_model.addItem(name)


# Start application
app = QApplication(sys.argv)
qt_register_fonts()
Expand Down
Loading

0 comments on commit da5410a

Please sign in to comment.