Skip to content

Commit

Permalink
all new colors and H20 support
Browse files Browse the repository at this point in the history
  • Loading branch information
stefanmuller committed Nov 14, 2023
1 parent 6ce7c0c commit 50e10b4
Show file tree
Hide file tree
Showing 19 changed files with 1,144 additions and 663 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ You can source individual parts with the **Brickini LDraw Part HDA** or import e

## Features
- **Import individual bricks & entire models**
- **Logo on studs:**
- **Logo on studs**
- **LDR, L3B & MPD file support:** mpd files contain multiple sub models, referenced by a main model
- **Instancing support:** duplicate bricks are automatically packed and the color is read from the point data
- **Imperfections:** if importing a model, the bricks are not perfectly stacked/aligned to each other, for a more realistic look and bricks can randomly yellow due to age.
Expand All @@ -21,10 +21,12 @@ You can source individual parts with the **Brickini LDraw Part HDA** or import e
- **Auto generate textures for prints/stickers** for modern cg workflows
- **Auto uvs**
- **Solaris + Karma example scene** contains MaterialX shader showcasing how to create high quality renderings
- **ACES colorspace:** LDraw colors are converted to acescg

## Requirements
- Houdini 19.5
- Houdini 20
- SideFX Labs for auto uv feature
- OCIO ACES colorspace configuration for Houdini

## Installation

Expand Down
Binary file added config/Icons/brickini_karma.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file added otls/brickini.brickini_karma_mat.1.1.hdalc
Binary file not shown.
Binary file removed otls/brickini.brickini_ldraw_part.1.16.hdalc
Binary file not shown.
Binary file added otls/brickini.brickini_ldraw_part.2.0.hdalc
Binary file not shown.
Binary file not shown.
84 changes: 84 additions & 0 deletions python3.10libs/convert_ldconfig_to_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from pathlib import Path
import re
import json

resources = Path(__file__).resolve().parent.parent / 'resources'
ld_colors = resources / 'ld_colors.json'
brickini_colors = resources / 'brickini_colors.json'
ldconfig = '/home/stefan/ldraw/LDConfig.ldr'

def hex_to_acescg(hex_value):
# Convert hex to float rgb
r = int(hex_value[0:2], 16) / 255
g = int(hex_value[2:4], 16) / 255
b = int(hex_value[4:6], 16) / 255

# convert srgb to linear
r = (r / 12.92) if r <= 0.04045 else ((r + 0.055) / 1.055) ** 2.4
g = (g / 12.92) if g <= 0.04045 else ((g + 0.055) / 1.055) ** 2.4
b = (b / 12.92) if b <= 0.04045 else ((b + 0.055) / 1.055) ** 2.4

# thanks to Michael on acescentral.com
# Generated using the Bradford CAT on https://www.colour-science.org:8010/apps/rgb_colourspace_transformation_matrix
m = [[0.613132422390542, 0.339538015799666, 0.047416696048269],
[0.070124380833917, 0.916394011313573, 0.013451523958235],
[0.020587657528185, 0.109574571610682, 0.869785404035327]]

#convert linear rgb to acescg
acescg = [m[0][0] * r + m[0][1] * g + m[0][2] * b,
m[1][0] * r + m[1][1] * g + m[1][2] * b,
m[2][0] * r + m[2][1] * g + m[2][2] * b]

return acescg

def read_ld_config():
color_dict = {}
# regular expressions for extracting information
category_pattern = re.compile(r'0\s+//\s+LDraw\s+(.+)\s+Colours')
color_pattern = re.compile(r'^0\s+!COLOUR\s+(\w+)\s+CODE\s+(\d+)\s+VALUE\s+([^\s]+)')

with open(ldconfig, 'r') as f:
for line in f:

# check if the line contains the category information
category_match = category_pattern.match(line)
if category_match:
current_category = category_match.group(1)

# check if the line contains color information
match = color_pattern.match(line)
if match:
name, code, value = match.groups()
value= value.replace('#','')
# print (name)
rgb = hex_to_acescg(value)

color_dict[code] = {
'category': current_category,
'name': name,
'rgb': rgb
}
return color_dict

def update_to_brickini_colors(color_dict):
# load brickini_colors.json
with open(brickini_colors, 'r') as f:
ld_colors_dict = json.load(f)

#update/overwrite original ldraw colors
color_dict.update(ld_colors_dict)

return color_dict

# The way this works for now is that I take the LDConfig.ldr that comes with lDraw
# and convert it to a more easily digestible json, alongside aces conversion.
# However, some colors aren't quite accurate (mostly transparent colors are way too dark)
# therefore I have a brickini_colors.json that I manually fill up with better colors.
# If a color exists in that file, it overwrites the one coming from LDraw.
# For now it's just transparent colors, but might expand in the future...

color_dict = read_ld_config()
color_dict = update_to_brickini_colors(color_dict)

with open(ld_colors, 'w') as f:
json.dump(color_dict, f, indent=4)
24 changes: 20 additions & 4 deletions python3.9libs/ldraw.py → python3.10libs/ldraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,16 @@ def find_value_index(list_of_lists, search_value):
for index, sublist in enumerate(list_of_lists):
if search_value in sublist.menuItems():
return index
return -1
return 0 # if not found we fallback to solid material group

def get_color_name(color_code):
_color_lib = color_lib()
entry = _color_lib.get(color_code)
if entry:
color_name = entry.get('name')
else:
color_name = 'Not_Found'
return color_name

def place_part(color_code, part, geo_node):
'''Places a part + color sop in the nodegraph.'''
Expand All @@ -94,7 +103,7 @@ def place_part(color_code, part, geo_node):

if not part_list[part]:
part_sop_name = strip_special_characters(part_name)
part_sop_name = 'part_{0}_1'.format(part_sop_name)
part_sop_name = 'bldp_{0}_'.format(part_sop_name)
part_sop = geo_node.createNode('brickini_ldraw_part', part_sop_name)

part_sop.parm('part').set(part_name)
Expand All @@ -106,7 +115,9 @@ def place_part(color_code, part, geo_node):
part_sop = part_list[part]

# Deliberately not using the more convenient .createOutputNode() method as for some reason it's much slower than connecting it manually afterwards
material_sop = geo_node.createNode('brickini_material', 'brickini_material1', run_init_scripts=True)
color_name = get_color_name(color_code)
material_sop_name = 'bm_{0}'.format(color_name)
material_sop = geo_node.createNode('brickini_material', material_sop_name, run_init_scripts=True)
material_sop.setInput(0, part_sop, 0)

mat_parms = []
Expand All @@ -119,7 +130,12 @@ def place_part(color_code, part, geo_node):
result_index = find_value_index(mat_parms, color_code)

material_sop.parm('material_group').set(result_index)
mat_parms[result_index].set(color_code)

# if color_code not valid parm value we fallback to 0 (black)
try:
mat_parms[result_index].set(color_code)
except:
mat_parms[result_index].set(0)

return material_sop

Expand Down
File renamed without changes.
52 changes: 0 additions & 52 deletions python3.9libs/convert_ldconfig_to_json.py

This file was deleted.

Loading

0 comments on commit 50e10b4

Please sign in to comment.