1568 lines
63 KiB
Python
Executable File
1568 lines
63 KiB
Python
Executable File
import sys, string, math, types
|
|
from pandac.PandaModules import *
|
|
import direct.gui.DirectGuiGlobals as DGG
|
|
from direct.gui.DirectGui import *
|
|
|
|
from PieMenu import *
|
|
from ScrollMenu import *
|
|
|
|
dnaDirectory = Filename.expandFrom(base.config.GetString("dna-directory", "$TTMODELS/src/dna"))
|
|
|
|
|
|
# Colors used by all color menus
|
|
DEFAULT_COLORS = [
|
|
Vec4(1, 1, 1, 1),
|
|
Vec4(0.75, 0.75, 0.75, 1.0),
|
|
Vec4(0.5, 0.5, 0.5, 1.0),
|
|
Vec4(0.25, 0.25, 0.25, 1.0)
|
|
]
|
|
# The list of items with color attributes
|
|
COLOR_TYPES = ['wall_color', 'window_color',
|
|
'window_awning_color', 'sign_color', 'door_color',
|
|
'door_awning_color', 'cornice_color',
|
|
'prop_color']
|
|
# The list of dna components maintained in the style attribute dictionary
|
|
DNA_TYPES = ['wall', 'window', 'sign', 'door_double', 'door_single', 'cornice', 'toon_landmark',
|
|
'prop', 'street']
|
|
BUILDING_TYPES = ['10_10', '20', '10_20', '20_10', '10_10_10',
|
|
'4_21', '3_22', '4_13_8', '3_13_9', '10',
|
|
'12_8', '13_9_8', '4_10_10', '4_10', '4_20',
|
|
]
|
|
BUILDING_HEIGHTS = [10, 14, 20, 24, 25, 30]
|
|
NUM_WALLS = [1, 2, 3]
|
|
LANDMARK_SPECIAL_TYPES = ['', 'hq', 'gagshop', 'clotheshop', 'petshop', 'kartshop']
|
|
|
|
OBJECT_SNAP_POINTS = {
|
|
'street_5x20': [(Vec3(5.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_10x20': [(Vec3(10.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_20x20': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_30x20': [(Vec3(30.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_40x20': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_80x20': [(Vec3(80.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_5x40': [(Vec3(5.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_10x40': [(Vec3(10.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_20x40': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_20x40_15': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_30x40': [(Vec3(30.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_40x40': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_20x60': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_40x60': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_40x40_15': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_80x40': [(Vec3(80.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_angle_30': [(Vec3(0), Vec3(-30, 0, 0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_angle_45': [(Vec3(0), Vec3(-45, 0, 0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_angle_60': [(Vec3(0), Vec3(-60, 0, 0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_inner_corner': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_outer_corner': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_full_corner': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_tight_corner': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_tight_corner_mirror': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_double_corner': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_curved_corner': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_curved_corner_15': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_t_intersection': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_y_intersection': [(Vec3(30.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_street_20x20': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_street_40x40': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_sidewalk_20x20': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_sidewalk_40x40': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_divided_transition': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_divided_40x70': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_divided_transition_15': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_divided_40x70_15': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_stairs_40x10x5': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_4way_intersection': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_incline_40x40x5': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_square_courtyard': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_70': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_70_exit': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_90': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_90_exit': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_70_15': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_70_15_exit': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_90_15': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_courtyard_90_15_exit': [(Vec3(0.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_50_transition': [(Vec3(10.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_20x50': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_40x50': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_keyboard_10x40': [(Vec3(10.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_keyboard_20x40': [(Vec3(20.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_keyboard_40x40': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
'street_sunken_40x40': [(Vec3(40.0, 0, 0), Vec3(0)),
|
|
(Vec3(0), Vec3(0))],
|
|
}
|
|
|
|
|
|
# Precompute class types for type comparisons
|
|
DNA_CORNICE = DNACornice.getClassType()
|
|
DNA_DOOR = DNADoor.getClassType()
|
|
DNA_FLAT_DOOR = DNAFlatDoor.getClassType()
|
|
DNA_FLAT_BUILDING = DNAFlatBuilding.getClassType()
|
|
DNA_NODE = DNANode.getClassType()
|
|
DNA_GROUP = DNAGroup.getClassType()
|
|
DNA_VIS_GROUP = DNAVisGroup.getClassType()
|
|
DNA_LANDMARK_BUILDING = DNALandmarkBuilding.getClassType()
|
|
DNA_NODE = DNANode.getClassType()
|
|
DNA_PROP = DNAProp.getClassType()
|
|
DNA_SIGN = DNASign.getClassType()
|
|
DNA_SIGN_BASELINE = DNASignBaseline.getClassType()
|
|
DNA_SIGN_TEXT = DNASignText.getClassType()
|
|
DNA_SIGN_GRAPHIC = DNASignGraphic.getClassType()
|
|
DNA_STREET = DNAStreet.getClassType()
|
|
DNA_WALL = DNAWall.getClassType()
|
|
DNA_WINDOWS = DNAWindows.getClassType()
|
|
|
|
# DNA Utility functions (possible class extensions?)
|
|
def DNARemoveChildren(dnaObject):
|
|
""" Utility function to delete all the children of a DNANode """
|
|
children = []
|
|
for i in range(dnaObject.getNumChildren()):
|
|
children.append(dnaObject.at(i))
|
|
for child in children:
|
|
dnaObject.remove(child)
|
|
DNASTORE.removeDNAGroup(child)
|
|
|
|
def DNARemoveChildOfClass(dnaNode, classType, childNum = 0):
|
|
""" Remove the nth object of that type you come across """
|
|
childCount = 0
|
|
for i in range(dnaNode.getNumChildren()):
|
|
child = dnaNode.at(i)
|
|
if DNAClassEqual(child, classType):
|
|
if childCount == childNum:
|
|
dnaNode.remove(child)
|
|
DNASTORE.removeDNAGroup(child)
|
|
return 1
|
|
childCount = childCount + 1
|
|
# None found
|
|
return 0
|
|
|
|
def DNARemoveAllChildrenOfClass(dnaNode, classType):
|
|
""" Remove the objects of that type """
|
|
children = []
|
|
for i in range(dnaNode.getNumChildren()):
|
|
child=dnaNode.at(i)
|
|
if DNAClassEqual(child, classType):
|
|
children.append(child)
|
|
for child in children:
|
|
dnaNode.remove(child)
|
|
DNASTORE.removeDNAGroup(child)
|
|
|
|
def DNAGetChildren(dnaNode, classType=None):
|
|
""" Return the objects of that type """
|
|
children = []
|
|
for i in range(dnaNode.getNumChildren()):
|
|
child=dnaNode.at(i)
|
|
if ((not classType)
|
|
or DNAClassEqual(child, classType)):
|
|
children.append(child)
|
|
return children
|
|
|
|
def DNAGetChild(dnaObject, type = DNA_NODE, childNum = 0):
|
|
childCount = 0
|
|
for i in range(dnaObject.getNumChildren()):
|
|
child = dnaObject.at(i)
|
|
if DNAClassEqual(child, type):
|
|
if childCount == childNum:
|
|
return child
|
|
childCount = childCount + 1
|
|
# Not found
|
|
return None
|
|
|
|
def DNAGetChildRecursive(dnaObject, type = DNA_NODE, childNum = 0):
|
|
childCount = 0
|
|
for i in range(dnaObject.getNumChildren()):
|
|
child = dnaObject.at(i)
|
|
if DNAClassEqual(child, type):
|
|
if childCount == childNum:
|
|
return child
|
|
childCount = childCount + 1
|
|
else:
|
|
child = DNAGetChildRecursive(child, type, childNum-childCount)
|
|
if child:
|
|
return child
|
|
|
|
# Not found
|
|
return None
|
|
|
|
def DNAGetChildOfClass(dnaNode, classType):
|
|
for i in range(dnaNode.getNumChildren()):
|
|
child = dnaNode.at(i)
|
|
if DNAClassEqual(child, classType):
|
|
return child
|
|
# Not found
|
|
return None
|
|
|
|
def DNAGetClassType(dnaObject):
|
|
return dnaObject.__class__.getClassType()
|
|
|
|
def DNAClassEqual(dnaObject, classType):
|
|
return DNAGetClassType(dnaObject).eq(classType)
|
|
|
|
def DNAIsDerivedFrom(dnaObject, classType):
|
|
return DNAGetClassType(dnaObject).isDerivedFrom(classType)
|
|
|
|
def DNAGetWallHeights(aDNAFlatBuilding):
|
|
""" Get a list of wall heights for a given flat building """
|
|
# Init variables
|
|
heightList = []
|
|
offsetList = []
|
|
offset = 0.0
|
|
# Compute wall heights
|
|
for i in range(aDNAFlatBuilding.getNumChildren()):
|
|
child = aDNAFlatBuilding.at(i)
|
|
if DNAClassEqual(child, DNA_WALL):
|
|
height = child.getHeight()
|
|
heightList.append(height)
|
|
offsetList.append(offset)
|
|
offset = offset + height
|
|
return heightList, offsetList
|
|
|
|
def DNAGetBaselineString(baseline):
|
|
s=""
|
|
for i in range(baseline.getNumChildren()):
|
|
child = baseline.at(i)
|
|
if DNAClassEqual(child, DNA_SIGN_TEXT):
|
|
s=s+child.getLetters()
|
|
elif DNAClassEqual(child, DNA_SIGN_GRAPHIC):
|
|
s=s+'['+child.getCode()+']'
|
|
return s
|
|
|
|
def DNASetBaselineString(baseline, text):
|
|
# TODO: Instead of removing all the text and replacing it,
|
|
# replace each text item and then add or remove at the end.
|
|
# This should allow inlined graphics to stay in place.
|
|
# end of todo.
|
|
DNARemoveAllChildrenOfClass(baseline, DNA_SIGN_TEXT)
|
|
|
|
# We can't just blindly iterate through the text, because it might
|
|
# be utf-8 encoded, meaning some characters are represented using
|
|
# multi-byte sequences. Instead, create a TextNode and use it to
|
|
# iterate through the characters of the text.
|
|
t = TextNode('')
|
|
t.setText(text)
|
|
for i in range(t.getNumChars()):
|
|
ch = t.getEncodedChar(i)
|
|
text=DNASignText("text")
|
|
text.setLetters(ch)
|
|
baseline.add(text)
|
|
|
|
class LevelStyleManager:
|
|
"""Class which reads in style files and manages class variables"""
|
|
def __init__(self, NEIGHBORHOODS = [], NEIGHBORHOOD_CODES = {} ):
|
|
self.NEIGHBORHOODS = NEIGHBORHOODS
|
|
self.NEIGHBORHOOD_CODES = NEIGHBORHOOD_CODES
|
|
# The main dictionary holding all attribute objects
|
|
self.attributeDictionary = {}
|
|
# Create the style samples
|
|
self.createBaselineStyleAttributes()
|
|
self.createWallStyleAttributes()
|
|
self.createBuildingStyleAttributes()
|
|
self.createColorAttributes()
|
|
self.createDNAAttributes()
|
|
self.createMiscAttributes()
|
|
|
|
# BASELINE STYLE FUNCTIONS
|
|
def createBaselineStyleAttributes(self):
|
|
"""
|
|
Create a baselineStyle entry in the attribute dictionary
|
|
This will be a dictionary of style attributes, one per neighborhood
|
|
"""
|
|
# First create an empty dictionary
|
|
dict = self.attributeDictionary['baseline_style'] = {}
|
|
# Create a attribute object for each neighborhood
|
|
for neighborhood in self.NEIGHBORHOODS:
|
|
attribute = LevelAttribute('baseline_style')
|
|
attribute.setDict(
|
|
# Create a baseline style dictionary for each neighborhood
|
|
self.createBaselineStyleDictionary(neighborhood))
|
|
# Using this dictionary, create style pie menus
|
|
attribute.setMenu(
|
|
self.createBaselineStyleMenu(neighborhood, attribute.getDict()))
|
|
dict[neighborhood] = attribute
|
|
|
|
def createBaselineStyleDictionary(self, neighborhood):
|
|
"""
|
|
Create a dictionary of baseline styles for a neighborhood
|
|
"""
|
|
filename = neighborhood + '_baseline_styles.txt'
|
|
print 'Loading baseline styles from: ' + filename
|
|
styleData = self.getStyleFileData(filename)
|
|
return self.initializeBaselineStyleDictionary(styleData, neighborhood)
|
|
|
|
def initializeBaselineStyleDictionary(self, styleData, neighborhood):
|
|
"""
|
|
Fill in the baseline style dictionary with data from the style file
|
|
"""
|
|
styleDictionary = {}
|
|
styleCount = 0
|
|
code = self.NEIGHBORHOOD_CODES[neighborhood]
|
|
while styleData:
|
|
l = styleData[0]
|
|
if l == 'baselineStyle':
|
|
# Start of new style, strip off first line then extract style
|
|
style, styleData = self.extractBaselineStyle(styleData)
|
|
style.name = code + '_baseline_style_' + `styleCount`
|
|
# Store style in dictionary
|
|
styleDictionary[style.name] = style
|
|
styleCount = styleCount + 1
|
|
# Move to next line
|
|
styleData = styleData[1:]
|
|
return styleDictionary
|
|
|
|
def extractBaselineStyle(self, styleData):
|
|
"""
|
|
Pull out one style from a list of style data. Will keep
|
|
processing data until endBaselineStyle of end of data is reached.
|
|
Returns a baseline style and remaining styleData.
|
|
"""
|
|
# Create default style
|
|
style = DNABaselineStyle()
|
|
# Strip off first line
|
|
styleData = styleData[1:]
|
|
while styleData:
|
|
l = styleData[0]
|
|
if l == 'endBaselineStyle':
|
|
# End of style found, break out of while loop
|
|
# Note, endBaselineStyle line is *not* stripped off
|
|
return style, styleData
|
|
else:
|
|
pair = map(string.strip, l.split(':'))
|
|
if style.__dict__.has_key(pair[0]):
|
|
pair_0 = pair[0]
|
|
# Convert some numerical values
|
|
if (pair_0 == 'color'
|
|
or pair_0 == 'kern'
|
|
or pair_0 == 'wiggle'
|
|
or pair_0 == 'stumble'
|
|
or pair_0 == 'stomp'
|
|
or pair_0 == 'curve'
|
|
or pair_0 == 'x'
|
|
or pair_0 == 'z'
|
|
or pair_0 == 'scaleX'
|
|
or pair_0 == 'scaleZ'
|
|
or pair_0 == 'roll'):
|
|
style[pair_0] = eval(pair[1])
|
|
else:
|
|
style[pair_0] = pair[1]
|
|
else:
|
|
print 'extractBaselineStyle: Invalid Key'
|
|
print pair[0]
|
|
styleData = styleData[1:]
|
|
# No end of style found, return style data as is
|
|
return style, None
|
|
|
|
def createBaselineStyleMenu(self, neighborhood, dictionary):
|
|
"""
|
|
Create a baseline style pie menu
|
|
"""
|
|
numItems = len(dictionary)
|
|
newStyleMenu = hidden.attachNewNode(neighborhood + '_style_menu')
|
|
radius = 0.7
|
|
angle = deg2Rad(360.0/numItems)
|
|
keys = dictionary.keys()
|
|
keys.sort()
|
|
styles = map(lambda x, d = dictionary: d[x], keys)
|
|
sf = 0.1
|
|
aspectRatio = (base.direct.dr.getWidth()/float(base.direct.dr.getHeight()))
|
|
for i in range(numItems):
|
|
# Get the node
|
|
node = self.createBaselineStyleSample(styles[i])
|
|
bounds = node.getBounds()
|
|
center = bounds.getCenter()
|
|
center = center * sf
|
|
# Reposition it
|
|
node.setPos((radius * math.cos(i * angle)) - center[0],
|
|
0.0,
|
|
((radius * aspectRatio * math.sin(i * angle)) -
|
|
center[2]))
|
|
# Scale it
|
|
node.setScale(sf)
|
|
# Add it to the styleMenu
|
|
node.reparentTo(newStyleMenu)
|
|
# Scale the whole shebang down by 0.5
|
|
newStyleMenu.setScale(0.5)
|
|
# Create and return a pie menu
|
|
return PieMenu(newStyleMenu, styles)
|
|
|
|
def createBaselineStyleSample(self, baselineStyle):
|
|
"""
|
|
Create a style sample using the DNA info in the style
|
|
"""
|
|
baseline = DNASignBaseline()
|
|
# Set some example text:
|
|
DNASetBaselineString(baseline, "Example Text")
|
|
baselineStyle.copyTo(baseline)
|
|
return baseline.traverse(hidden, DNASTORE, 1)
|
|
|
|
# WALL STYLE FUNCTIONS
|
|
def createWallStyleAttributes(self):
|
|
"""
|
|
Create a wallStyle entry in the attribute dictionary
|
|
This will be a dictionary of style attributes, one per neighborhood
|
|
"""
|
|
# First create an empty dictionary
|
|
dict = self.attributeDictionary['wall_style'] = {}
|
|
# Create a attribute object for each neighborhood
|
|
for neighborhood in self.NEIGHBORHOODS:
|
|
attribute = LevelAttribute('wall_style')
|
|
attribute.setDict(
|
|
# Create a wall style dictionary for each neighborhood
|
|
self.createWallStyleDictionary(neighborhood))
|
|
# Using this dictionary, create color pie menus
|
|
attribute.setMenu(
|
|
self.createWallStyleMenu(neighborhood, attribute.getDict()))
|
|
dict[neighborhood] = attribute
|
|
|
|
def createWallStyleDictionary(self, neighborhood):
|
|
"""
|
|
Create a dictionary of wall styles for a neighborhood
|
|
"""
|
|
filename = neighborhood + '_wall_styles.txt'
|
|
print 'Loading wall styles from: ' + filename
|
|
styleData = self.getStyleFileData(filename)
|
|
return self.initializeWallStyleDictionary(styleData, neighborhood)
|
|
|
|
def initializeWallStyleDictionary(self, styleData, neighborhood):
|
|
"""
|
|
Fill in the wall style dictionary with data from the style file
|
|
"""
|
|
styleDictionary = {}
|
|
styleCount = 0
|
|
code = self.NEIGHBORHOOD_CODES[neighborhood]
|
|
while styleData:
|
|
l = styleData[0]
|
|
if l == 'wallStyle':
|
|
# Start of new style, strip off first line then extract style
|
|
style, styleData = self.extractWallStyle(styleData)
|
|
style.name = code + '_wall_style_' + `styleCount`
|
|
# Store style in dictionary
|
|
styleDictionary[style.name] = style
|
|
styleCount = styleCount + 1
|
|
# Move to next line
|
|
styleData = styleData[1:]
|
|
return styleDictionary
|
|
|
|
def extractWallStyle(self, styleData):
|
|
"""
|
|
Pull out one style from a list of style data. Will keep
|
|
processing data until endWallStyle of end of data is reached.
|
|
Returns a wall style and remaining styleData.
|
|
"""
|
|
# Create default style
|
|
style = DNAWallStyle()
|
|
# Strip off first line
|
|
styleData = styleData[1:]
|
|
while styleData:
|
|
l = styleData[0]
|
|
if l == 'endWallStyle':
|
|
# End of style found, break out of while loop
|
|
# Note, endWallStyle line is *not* stripped off
|
|
return style, styleData
|
|
else:
|
|
pair = map(string.strip, l.split(':'))
|
|
if style.__dict__.has_key(pair[0]):
|
|
# Convert colors and count strings to numerical values
|
|
if ((string.find(pair[0],'_color') >= 0) or
|
|
(string.find(pair[0],'_count') >= 0)):
|
|
style[pair[0]] = eval(pair[1])
|
|
else:
|
|
style[pair[0]] = pair[1]
|
|
else:
|
|
print 'getStyleDictionaryFromStyleData: Invalid Key'
|
|
print pair[0]
|
|
styleData = styleData[1:]
|
|
# No end of style found, return style data as is
|
|
return style, None
|
|
|
|
def createWallStyleMenu(self, neighborhood, dictionary):
|
|
"""
|
|
Create a wall style pie menu
|
|
"""
|
|
numItems = len(dictionary)
|
|
newStyleMenu = hidden.attachNewNode(neighborhood + '_style_menu')
|
|
radius = 0.7
|
|
angle = deg2Rad(360.0/numItems)
|
|
keys = dictionary.keys()
|
|
keys.sort()
|
|
styles = map(lambda x, d = dictionary: d[x], keys)
|
|
sf = 0.03
|
|
aspectRatio = (base.direct.dr.getWidth()/float(base.direct.dr.getHeight()))
|
|
for i in range(numItems):
|
|
# Get the node
|
|
node = self.createWallStyleSample(styles[i])
|
|
bounds = node.getBounds()
|
|
center = bounds.getCenter()
|
|
center = center * sf
|
|
# Reposition it
|
|
node.setPos((radius * math.cos(i * angle)) - center[0],
|
|
0.0,
|
|
((radius * aspectRatio * math.sin(i * angle)) -
|
|
center[2]))
|
|
# Scale it
|
|
node.setScale(sf)
|
|
# Add it to the styleMenu
|
|
node.reparentTo(newStyleMenu)
|
|
# Scale the whole shebang down by 0.5
|
|
newStyleMenu.setScale(0.5)
|
|
# Create and return a pie menu
|
|
return PieMenu(newStyleMenu, styles)
|
|
|
|
def createWallStyleSample(self, wallStyle):
|
|
"""
|
|
Create a style sample using the DNA info in the style
|
|
"""
|
|
bldg = DNAFlatBuilding()
|
|
bldgStyle = DNAFlatBuildingStyle(styleList = [(wallStyle, 10.0)])
|
|
self.setDNAFlatBuildingStyle(bldg, bldgStyle, width = 12.0,
|
|
name = 'wall_style_sample')
|
|
return bldg.traverse(hidden, DNASTORE, 1)
|
|
|
|
# BUILDING STYLE FUNCTIONS
|
|
def createBuildingStyleAttributes(self):
|
|
"""
|
|
Create a buildingStyle entry in the attribute dictionary
|
|
This will be a dictionary of style attributes, one per neighborhood
|
|
This information is sorted and stored by num walls, building height,
|
|
and building type (e.g. 10_20, or 10_10_10).
|
|
"""
|
|
# First create an attribute which holds one dictionary per
|
|
# neighborhood which stores all of the styles for each neighborhood.
|
|
styleDict = self.attributeDictionary['building_style_all'] = {}
|
|
# Keep track of all building types
|
|
typeDict = {}
|
|
masterTypeList = []
|
|
for neighborhood in self.NEIGHBORHOODS:
|
|
attribute = LevelAttribute('building_style_all')
|
|
# Create a building style dictionary for each neighborhood
|
|
attribute.setDict(
|
|
self.createBuildingStyleDictionary(neighborhood))
|
|
# Using this dictionary, create building style pie menus
|
|
attribute.setMenu(
|
|
self.createBuildingStyleMenu(neighborhood,
|
|
attribute.getDict()))
|
|
# Store attribute in dictionary keyed by neighborhood
|
|
styleDict[neighborhood] = attribute
|
|
# Generate a list of building types for this neighborhood
|
|
# and a list of building types for all neighborhoods,
|
|
# to be used in creating attribute dicts below
|
|
typeList = typeDict[neighborhood] = []
|
|
for style in attribute.getList():
|
|
heightType = string.strip(string.split(style.name, ':')[1])
|
|
if heightType not in typeList:
|
|
typeList.append(heightType)
|
|
if heightType not in masterTypeList:
|
|
masterTypeList.append(heightType)
|
|
|
|
# Now sort styles according to the building type and number of walls
|
|
# in the building style. Each of these attributes is also sorted by
|
|
# neighborhood
|
|
for i in masterTypeList:
|
|
typeKey = i + '_styles'
|
|
self.attributeDictionary[typeKey] = {}
|
|
for i in NUM_WALLS:
|
|
numWallKey = `i` + '_wall_styles'
|
|
self.attributeDictionary[numWallKey] = {}
|
|
# Also sort height lists according to total height of the building
|
|
for i in BUILDING_HEIGHTS:
|
|
heightKey = `i` + '_ft_wall_heights'
|
|
self.attributeDictionary[heightKey] = {}
|
|
# Now distribute data for each neighborhood
|
|
for neighborhood in self.NEIGHBORHOODS:
|
|
# Create temp dicts to accumulate styles and heights for that
|
|
# neighborhood
|
|
typeAttributes = {}
|
|
numWallsAttributes = {}
|
|
heightAttributes = {}
|
|
# Store atributes for the different categories
|
|
# Building type
|
|
for i in typeDict[neighborhood]:
|
|
typeAttrName = neighborhood + '_' + i + '_styles'
|
|
typeAttributes[i] = LevelAttribute(typeAttrName)
|
|
# Number of walls
|
|
for i in NUM_WALLS:
|
|
styleAttrName = neighborhood + '_' + `i` + '_wall_styles'
|
|
numWallsAttributes[i] = LevelAttribute(styleAttrName)
|
|
# Building height
|
|
for i in BUILDING_HEIGHTS:
|
|
heightAttrName = neighborhood + '_' + `i` + '_ft_wall_heights'
|
|
heightAttributes[i] = LevelAttribute(heightAttrName)
|
|
# Sort through the styles and store in separate lists
|
|
for style in styleDict[neighborhood].getList():
|
|
# Put in code for number of walls into building styles
|
|
heightType = string.strip(string.split(style.name, ':')[1])
|
|
heightList = map(string.atof, string.split(heightType, '_'))
|
|
numWalls = len(heightList)
|
|
# This one stores styles sorted by type
|
|
typeAttributes[heightType].add(style)
|
|
# This one stores styles sorted by number of walls
|
|
numWallsAttributes[numWalls].add(style)
|
|
# A record of all the unique height lists
|
|
height = calcHeight(heightList)
|
|
if heightList not in heightAttributes[height].getList():
|
|
heightAttributes[height].add(heightList)
|
|
# Now store these sorted style and height attributes
|
|
# in the appropriate top-level attribute dictionary
|
|
for i in typeDict[neighborhood]:
|
|
# Styles
|
|
typeKey = i + '_styles'
|
|
self.attributeDictionary[typeKey][neighborhood] = (
|
|
typeAttributes[i])
|
|
for i in NUM_WALLS:
|
|
# Styles
|
|
numWallKey = `i` + '_wall_styles'
|
|
self.attributeDictionary[numWallKey][neighborhood] = (
|
|
numWallsAttributes[i])
|
|
for i in BUILDING_HEIGHTS:
|
|
# Heights
|
|
heightKey = `i` + '_ft_wall_heights'
|
|
self.attributeDictionary[heightKey][neighborhood] = (
|
|
heightAttributes[i])
|
|
|
|
def createBuildingStyleDictionary(self, neighborhood):
|
|
"""
|
|
Create a dictionary of wall styles for a neighborhood
|
|
"""
|
|
filename = neighborhood + '_building_styles.txt'
|
|
print 'Loading building styles from: ' + filename
|
|
styleData = self.getStyleFileData(filename)
|
|
return self.initializeBuildingStyleDictionary(styleData, neighborhood)
|
|
|
|
def initializeBuildingStyleDictionary(self, styleData, neighborhood):
|
|
"""
|
|
Fill in the building style dictionary with data from the style file
|
|
"""
|
|
# Create a dictionary of all building styles, this will later be
|
|
# split out into a separate dictionary for each building type
|
|
# e.g. 20, 10-10, 10-20....
|
|
bldgStyleDictionary = {}
|
|
styleCount = 0
|
|
code = self.NEIGHBORHOOD_CODES[neighborhood]
|
|
while styleData:
|
|
# Pull out first line
|
|
l = styleData[0]
|
|
if l[:13] == 'buildingStyle':
|
|
# Start with empty style list
|
|
bldgStyle = DNAFlatBuildingStyle(styleList = [])
|
|
# Extract height information found at end of line
|
|
heightCode = string.strip(string.split(l, ':')[1])
|
|
heightList = map(string.atof, string.split(heightCode, '_'))
|
|
# Construct name for building style. Tack on height code
|
|
# to be used later to split styles by heightCode
|
|
bldgStyle.name = (
|
|
code + '_building_style_' + `styleCount` +
|
|
':' + heightCode)
|
|
# Increment counter
|
|
styleCount = styleCount + 1
|
|
# Reset wall counter to zero
|
|
wallCount = 0
|
|
|
|
elif l == 'endBuildingStyle':
|
|
# Done, add new style to dictionary
|
|
bldgStyleDictionary[bldgStyle.name] = bldgStyle
|
|
|
|
elif l[:9] == 'wallStyle':
|
|
# Beginning of next wall style
|
|
wallStyle, styleData = self.extractWallStyle(styleData)
|
|
wallStyle.name = bldgStyle.name + '_wall_' + `wallCount`
|
|
try:
|
|
height = heightList[wallCount]
|
|
except IndexError:
|
|
height = 10.0
|
|
# Add wall style to building style
|
|
bldgStyle.add(wallStyle, height)
|
|
# Increment wall counter
|
|
wallCount = wallCount + 1
|
|
# Move to next line
|
|
styleData = styleData[1:]
|
|
return bldgStyleDictionary
|
|
|
|
def createBuildingStyleMenu(self, neighborhood, dictionary):
|
|
"""
|
|
Create a wall style pie menu
|
|
"""
|
|
numItems = len(dictionary)
|
|
newStyleMenu = hidden.attachNewNode(neighborhood + '_style_menu')
|
|
radius = 0.7
|
|
angle = deg2Rad(360.0/numItems)
|
|
keys = dictionary.keys()
|
|
keys.sort()
|
|
styles = map(lambda x, d = dictionary: d[x], keys)
|
|
sf = 0.02
|
|
aspectRatio = (base.direct.dr.getWidth()/float(base.direct.dr.getHeight()))
|
|
for i in range(numItems):
|
|
# Get the node
|
|
node = self.createBuildingStyleSample(styles[i])
|
|
bounds = node.getBounds()
|
|
center = bounds.getCenter()
|
|
center = center * sf
|
|
# Reposition it
|
|
node.setPos((radius * math.cos(i * angle)) - center[0],
|
|
0.0,
|
|
((radius * aspectRatio * math.sin(i * angle)) -
|
|
center[2]))
|
|
# Scale it
|
|
node.setScale(sf)
|
|
# Add it to the styleMenu
|
|
node.reparentTo(newStyleMenu)
|
|
# Scale the whole shebang down by 0.5
|
|
newStyleMenu.setScale(0.5)
|
|
# Create and return a pie menu
|
|
return PieMenu(newStyleMenu, styles)
|
|
|
|
def createBuildingStyleSample(self, bldgStyle):
|
|
"""
|
|
Create a style sample using the DNA info in the style
|
|
"""
|
|
bldg = DNAFlatBuilding()
|
|
self.setDNAFlatBuildingStyle(bldg, bldgStyle, width = 10.0,
|
|
name = 'building_style_sample')
|
|
return bldg.traverse(hidden, DNASTORE, 1)
|
|
|
|
def setDNAFlatBuildingStyle(self, fb, bldgStyle, width = 10.0,
|
|
heightList = None, name = 'building'):
|
|
""" Set DNAFlatBuilding style. """
|
|
# Remove flat building's children
|
|
DNARemoveChildren(fb)
|
|
# Update the name
|
|
fb.setName(name)
|
|
# Create the walls
|
|
styleList = bldgStyle.styleList
|
|
# Height list not specified, use styles default heights
|
|
if not heightList:
|
|
heightList = bldgStyle.heightList
|
|
for i in range(len(styleList)):
|
|
wallStyle = styleList[i]
|
|
# Get Style
|
|
if not wallStyle:
|
|
# If set to None use default style
|
|
wallStyle = DNAWallStyle()
|
|
# Try to get height
|
|
try:
|
|
wallHeight = heightList[i]
|
|
except IndexError:
|
|
wallHeight = 10.0
|
|
# Create wall accordingly
|
|
wall = DNAWall()
|
|
self.setDNAWallStyle(wall, wallStyle, wallHeight,
|
|
width = width)
|
|
# Add it to building DNA
|
|
fb.add(wall)
|
|
# Set the buildings width
|
|
fb.setWidth(width)
|
|
|
|
def setDNAWallStyle(self, wall, style, height = 10.0, width = None):
|
|
""" Set DNAWall to input style. """
|
|
# Remove wall's children
|
|
DNARemoveChildren(wall)
|
|
# Update wall attributes
|
|
wall.setCode(style['wall_texture'])
|
|
wall.setColor(style['wall_color'])
|
|
wall.setHeight(height)
|
|
# Add windows if necessary
|
|
if style['window_texture']:
|
|
windows = DNAWindows()
|
|
windowCount = style['window_count']
|
|
if width:
|
|
if (width < 15.0):
|
|
windowCount = min(1, windowCount)
|
|
windows.setWindowCount(windowCount)
|
|
# Set window's attributes
|
|
windows.setCode(style['window_texture'])
|
|
windows.setColor(style['window_color'])
|
|
# Add windows to the wall
|
|
wall.add(windows)
|
|
# Add a window awning if necessary
|
|
if style['window_awning_texture']:
|
|
awning = DNAProp()
|
|
# Update awning's attributes
|
|
awning.setCode(style['window_awning_texture'])
|
|
awning.setColor(style['window_awning_color'])
|
|
# Add awning to window
|
|
windows.add(awning)
|
|
# Add a door if necessary
|
|
if style['door_texture']:
|
|
door = DNAFlatDoor()
|
|
# Set the door's attributes
|
|
door.setCode(style['door_texture'])
|
|
door.setColor(style['door_color'])
|
|
# Add door to wall
|
|
wall.add(door)
|
|
# Add a door awning if necessary
|
|
if style['door_awning_texture']:
|
|
awning = DNAProp()
|
|
awning.setCode(style['door_awning_texture'])
|
|
awning.setColor(style['door_awning_color'])
|
|
door.add(awning)
|
|
# And a cornice if necessary
|
|
if style['cornice_texture']:
|
|
cornice = DNACornice()
|
|
# Set the cornice's attributes
|
|
cornice.setCode(style['cornice_texture'])
|
|
cornice.setColor(style['cornice_color'])
|
|
# Add cornice to wall
|
|
wall.add(cornice)
|
|
|
|
def printFlatBuildingStyle(self, building):
|
|
for i in range(building.getNumChildren()):
|
|
child = building.at(i)
|
|
if DNAClassEqual(child, DNA_WALL):
|
|
self.printWallStyle(child)
|
|
|
|
def printWallStyle(self, wall):
|
|
print 'wall_texture: ' + wall.getCode()
|
|
color = wall.getColor()
|
|
print ('wall_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
|
|
(color[0], color[1], color[2]))
|
|
for i in range(wall.getNumChildren()):
|
|
child = wall.at(i)
|
|
if DNAClassEqual(child, DNA_WINDOWS):
|
|
print 'window_texture: ' + child.getCode()
|
|
color = child.getColor()
|
|
print ('window_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
|
|
(color[0], color[1], color[2]))
|
|
# MRM: Check for awnings here
|
|
elif DNAClassEqual(child, DNA_DOOR):
|
|
print 'door_texture: ' + child.getCode()
|
|
color = child.getColor()
|
|
print ('door_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
|
|
(color[0], color[1], color[2]))
|
|
elif DNAClassEqual(child, DNA_FLAT_DOOR):
|
|
print 'door_texture: ' + child.getCode()
|
|
color = child.getColor()
|
|
print ('door_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
|
|
(color[0], color[1], color[2]))
|
|
elif DNAClassEqual(child, DNA_CORNICE):
|
|
print 'cornice_texture: ' + child.getCode()
|
|
color = child.getColor()
|
|
print ('cornice_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
|
|
(color[0], color[1], color[2]))
|
|
|
|
# COLOR PALETTE FUNCTIONS
|
|
def createColorAttributes(self):
|
|
# First compile color information for each neighborhood
|
|
colorDictionary = {}
|
|
colorMenuDictionary = {}
|
|
for neighborhood in self.NEIGHBORHOODS:
|
|
colorDictionary[neighborhood] = (
|
|
# Create a wall style dictionary for each neighborhood
|
|
self.createColorDictionary(neighborhood))
|
|
# Using this dictionary, create color pie menus
|
|
colorMenuDictionary[neighborhood] = (
|
|
self.createColorMenus(
|
|
neighborhood, colorDictionary[neighborhood]))
|
|
# Now store this info in the appropriate place in the attribute dict
|
|
for colorType in COLOR_TYPES:
|
|
neighborhoodDict = self.attributeDictionary[colorType] = {}
|
|
for neighborhood in self.NEIGHBORHOODS:
|
|
attribute = LevelAttribute(colorType)
|
|
dict = {}
|
|
# Add colors to attribute dictionary
|
|
colorList = colorDictionary[neighborhood][colorType]
|
|
for i in range(len(colorList)):
|
|
dict[i] = colorList[i]
|
|
attribute.setDict(dict)
|
|
attribute.setMenu(
|
|
colorMenuDictionary[neighborhood][colorType])
|
|
neighborhoodDict[neighborhood] = attribute
|
|
|
|
def createColorDictionary(self, neighborhood):
|
|
filename = neighborhood + '_colors.txt'
|
|
print 'Loading Color Palettes from: ' + filename
|
|
colorData = self.getStyleFileData(filename)
|
|
return self.getColorDictionary(colorData)
|
|
|
|
def getColorDictionary(self, colorData):
|
|
# Initialze neighborhod color dictionary
|
|
dict = {}
|
|
for colorType in COLOR_TYPES:
|
|
dict[colorType] = DEFAULT_COLORS[:]
|
|
# Add color information to appropriate sub-list
|
|
for line in colorData:
|
|
pair = map(string.strip, line.split(':'))
|
|
key = pair[0]
|
|
if dict.has_key(key):
|
|
dict[key].append(eval(pair[1]))
|
|
else:
|
|
print 'LevelStyleManager.getColorDictionary key not found'
|
|
return dict
|
|
|
|
def createColorMenus(self, neighborhood, dictionary):
|
|
menuDict = {}
|
|
keys = dictionary.keys()
|
|
for key in keys:
|
|
menuDict[key] = (
|
|
self.createColorMenu(neighborhood + key, dictionary[key]))
|
|
return menuDict
|
|
|
|
def createColorMenu(self, menuName, colorList, radius = 0.7, sf = 2.0):
|
|
# Create color chips for each color
|
|
numItems = len(colorList)
|
|
# Attach it to hidden for now
|
|
newColorMenu = hidden.attachNewNode(menuName + 'Menu')
|
|
# Compute the angle per item
|
|
angle = deg2Rad(360.0/float(numItems))
|
|
aspectRatio = (base.direct.dr.getWidth() / float(base.direct.dr.getHeight()))
|
|
# Attach the color chips to the new menu and adjust sizes
|
|
for i in range (numItems):
|
|
# Create a text node--just a card, really--of the right color.
|
|
tn = TextNode('colorChip')
|
|
tn.setFont(DGG.getDefaultFont())
|
|
tn.setTransform(Mat4.scaleMat(0.07, 0.07, 0.07 * aspectRatio))
|
|
tn.setCardColor(colorList[i])
|
|
tn.setCardActual(0, 1.1111, 0, 0.8333)
|
|
tn.setText(' ')
|
|
|
|
# Reposition it
|
|
card = tn.getCardTransformed()
|
|
center = (card[1] - card[0], card[3] - card[2])
|
|
|
|
node = newColorMenu.attachNewNode(tn)
|
|
node.setScale(sf)
|
|
node.setPos(radius * math.cos(i * angle) - center[0], 0.0,
|
|
((radius * aspectRatio * math.sin(i * angle)) -
|
|
center[1]))
|
|
# Scale the whole shebang down by 0.5
|
|
newColorMenu.setScale(0.5)
|
|
# Create and return resulting pie menu
|
|
return PieMenu(newColorMenu, colorList)
|
|
|
|
# DNA ATTRIBUTES
|
|
def createDNAAttributes(self):
|
|
# Create the DNA Attribute entries
|
|
# Most objects are oriented with graphical menu items
|
|
# Street and props aren't oiented and use text menus
|
|
for dnaType in DNA_TYPES:
|
|
# Create a dictionary of dna types
|
|
dict = {}
|
|
if ((dnaType == 'street') or (dnaType == 'prop') or
|
|
(dnaType == 'toon_landmark')):
|
|
dnaList = self.getCatalogCodes(dnaType)
|
|
if dnaType == 'prop':
|
|
dnaList = dnaList + self.getCatalogCodes('holiday_prop')
|
|
elif (dnaType == 'sign'):
|
|
dnaList = [''] + self.getCatalogCodes(dnaType)
|
|
elif (dnaType == 'wall'):
|
|
# Add in suit walls here for now
|
|
dnaList = ([None] +
|
|
self.getCatalogCodesSuffix(dnaType, '_ur') +
|
|
self.getCatalogCodesSuffix('suit_wall', '_ur'))
|
|
else:
|
|
dnaList = [None] + self.getCatalogCodesSuffix(dnaType, '_ur')
|
|
# Add dnaCodes to attribute dictionary
|
|
for i in range(len(dnaList)):
|
|
dict[i] = dnaList[i]
|
|
# Create a LevelAttribute
|
|
attribute = LevelAttribute(dnaType + '_texture')
|
|
attribute.setDict(dict)
|
|
# Prepend None to allow option of no item
|
|
if ((dnaType == 'street') or (dnaType == 'prop') or
|
|
(dnaType == 'toon_landmark')):
|
|
# attribute.setMenu(self.createTextPieMenu(dnaType, dnaList))
|
|
attribute.setMenu(self.createScrollMenu(dnaType, dnaList))
|
|
attribute.getMenu().createScrolledList()
|
|
elif (dnaType == 'wall'):
|
|
attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
|
|
sf = 0.25))
|
|
elif (dnaType == 'sign'):
|
|
attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
|
|
sf = 0.05))
|
|
elif (dnaType == 'door_double'):
|
|
attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
|
|
sf = 0.035))
|
|
elif (dnaType == 'door_single'):
|
|
attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
|
|
sf = 0.035))
|
|
elif (dnaType == 'cornice'):
|
|
attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
|
|
sf = 0.5))
|
|
elif (dnaType == 'window'):
|
|
attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
|
|
sf = 0.125))
|
|
else:
|
|
print 'unknown attribute'
|
|
# Add it to the attributeDictionary
|
|
self.attributeDictionary[dnaType + '_texture'] = attribute
|
|
|
|
def createDNAPieMenu(self, dnaType, dnaList, radius = 0.7, sf = 1.0):
|
|
# Get the currently available window options
|
|
numItems = len(dnaList)
|
|
# Create a top level node to hold the menu
|
|
newMenu = hidden.attachNewNode(dnaType + 'Menu')
|
|
# Compute angle increment per item
|
|
angle = deg2Rad(360.0/numItems)
|
|
aspectRatio = base.direct.dr.getWidth() /float(base.direct.dr.getHeight())
|
|
# Add items
|
|
for i in range(0, numItems):
|
|
if dnaList[i]:
|
|
# Get the node
|
|
node = DNASTORE.findNode(dnaList[i])
|
|
else:
|
|
node = None
|
|
if node:
|
|
# Add it to the window menu
|
|
# This instance was causing problems for dna nodes that were
|
|
# loaded as singletons, so I changed it to copyTo.
|
|
# path = node.instanceTo(newMenu)
|
|
path = node.copyTo(newMenu)
|
|
# Place menu nodes in a circle, offset each in X and Z
|
|
# by half node width/height
|
|
bounds = path.getBounds()
|
|
center = bounds.getCenter()
|
|
center = center * sf
|
|
path.setPos((radius * math.cos(i * angle)) - center[0],
|
|
0.0,
|
|
((radius * aspectRatio * math.sin(i * angle)) -
|
|
center[2]))
|
|
path.setScale(sf)
|
|
# Scale the whole shebang down by 0.5
|
|
newMenu.setScale(0.5)
|
|
# Create and return a pie menu
|
|
return PieMenu(newMenu, dnaList)
|
|
|
|
def createTextPieMenu(self, dnaType, textList, radius = 0.7, sf = 1.0):
|
|
numItems = len(textList)
|
|
# Create top level node for new menu
|
|
newMenu = hidden.attachNewNode(dnaType + 'Menu')
|
|
# Compute angle per item
|
|
if numItems == 0:
|
|
angle = 0.0
|
|
else:
|
|
angle = deg2Rad(360.0/numItems)
|
|
aspectRatio = base.direct.dr.getWidth()/float(base.direct.dr.getHeight())
|
|
# Add items
|
|
for i in range (numItems):
|
|
# Create text node for each item
|
|
if (textList[i] != None):
|
|
tn = TextNode('TextItem')
|
|
tn.setFont(DGG.getDefaultFont())
|
|
tn.setTransform(Mat4.scaleMat(0.07, 0.07, 0.07 * aspectRatio))
|
|
tn.setTextColor(0, 0, 0, 1)
|
|
tn.setCardColor(1, 1, 1, 1)
|
|
tn.setCardAsMargin(0.1, 0.1, 0.1, 0.1)
|
|
tn.setText(str(textList[i]))
|
|
|
|
# Reposition it
|
|
card = tn.getCardTransformed()
|
|
center = (card[1] - card[0], card[3] - card[2])
|
|
|
|
node = newMenu.attachNewNode(tn)
|
|
node.setScale(sf)
|
|
node.setPos(radius * math.cos(i * angle) - center[0], 0.0,
|
|
((radius * aspectRatio * math.sin(i * angle)) -
|
|
center[1]))
|
|
|
|
# Scale the whole shebang down by 0.5
|
|
newMenu.setScale(0.5)
|
|
# Create and return a pie menu
|
|
return PieMenu(newMenu, textList)
|
|
|
|
def createScrollMenu(self, dnaType, textList):
|
|
newMenu = hidden.attachNewNode(dnaType+'ScrollMenu')
|
|
|
|
return ScrollMenu(newMenu, textList)
|
|
|
|
# MISCELLANEOUS MENUS
|
|
def createMiscAttributes(self):
|
|
# Num windows menu
|
|
self.createMiscAttribute('window_count', [0, 1, 2, 3, 4])
|
|
# Building width menu
|
|
self.createMiscAttribute('building_width', [5, 10, 15, 15.6, 20, 20.7, 25])
|
|
# Building types
|
|
self.createMiscAttribute('building_type', BUILDING_TYPES)
|
|
# Building heights
|
|
self.createMiscAttribute('building_height', [10, 14, 20, 24, 25, 30])
|
|
# MRM: Need offset on these menus
|
|
# Wall orientation menu
|
|
self.createMiscAttribute('wall_orientation', ['ur','ul','dl','dr'])
|
|
# Wall height menu
|
|
self.createMiscAttribute('wall_height', [10, 20])
|
|
# Window orientation menu
|
|
self.createMiscAttribute('window_orientation', ['ur','ul', None, None])
|
|
# Sign orientation menu
|
|
self.createMiscAttribute('sign_orientation', ['ur','ul', None, None])
|
|
# Door orientation menu
|
|
self.createMiscAttribute('door_orientation', ['ur','ul', None, None])
|
|
# Cornice orientation menu
|
|
self.createMiscAttribute('cornice_orientation', ['ur','ul', None, None])
|
|
|
|
def createMiscAttribute(self, miscType, miscList, sf = 3.0):
|
|
# Create a dictionary from miscList
|
|
dict = {}
|
|
# Add items to attribute dictionary
|
|
for i in range(len(miscList)):
|
|
dict[i] = miscList[i]
|
|
# Create the miscellaneous Attribute entries
|
|
attribute = LevelAttribute(miscType)
|
|
attribute.setDict(dict)
|
|
# Now create a pie menu
|
|
attribute.setMenu(self.createTextPieMenu(miscType, miscList,
|
|
sf = sf))
|
|
# Add it to the attributeDictionary
|
|
self.attributeDictionary[miscType] = attribute
|
|
|
|
# GENERAL FUNCTIONS
|
|
def getStyleFileData(self, filename):
|
|
"""
|
|
Open the specified file and strip out unwanted whitespace and
|
|
empty lines. Return file as list, one file line per element.
|
|
"""
|
|
fname = Filename(dnaDirectory.getFullpath() +
|
|
'/stylefiles/' + filename)
|
|
|
|
# We use binary mode to avoid Windows' end-of-line convention
|
|
f = open(fname.toOsSpecific(), 'rb')
|
|
rawData = f.readlines()
|
|
f.close()
|
|
styleData = []
|
|
for line in rawData:
|
|
l = string.strip(line)
|
|
if l:
|
|
styleData.append(l)
|
|
return styleData
|
|
|
|
# UTILITY FUNCTIONS
|
|
def getAttribute(self, attribute):
|
|
""" Return specified attribute for current neighborhood """
|
|
levelAttribute = self.attributeDictionary[attribute]
|
|
# Get attribute for current neighborhood
|
|
if (type(levelAttribute) == types.DictionaryType):
|
|
levelAttribute = levelAttribute[self.getEditMode()]
|
|
return levelAttribute
|
|
|
|
# UTILITY FUNCTIONS
|
|
def hasAttribute(self, attribute):
|
|
""" Return specified attribute for current neighborhood """
|
|
if not self.attributeDictionary.has_key(attribute):
|
|
return 0
|
|
else:
|
|
levelAttribute = self.attributeDictionary[attribute]
|
|
# Get attribute for current neighborhood
|
|
if (type(levelAttribute) == types.DictionaryType):
|
|
editMode = self.getEditMode()
|
|
return levelAttribute.has_key(editMode)
|
|
else:
|
|
return 1
|
|
|
|
def getCatalogCode(self, category, i):
|
|
return DNASTORE.getCatalogCode(category, i)
|
|
|
|
def getCatalogCodes(self, category):
|
|
numCodes = DNASTORE.getNumCatalogCodes(category)
|
|
codes = []
|
|
for i in range(numCodes):
|
|
codes.append(DNASTORE.getCatalogCode(category, i))
|
|
return codes
|
|
|
|
def getCatalogCodesSuffix(self, category, suffix):
|
|
codes = self.getCatalogCodes(category)
|
|
orientedCodes = []
|
|
for code in codes:
|
|
if code[-3:] == suffix:
|
|
orientedCodes.append(code)
|
|
return orientedCodes
|
|
|
|
def setEditMode(self, mode):
|
|
"""Set current neighborhood editing mode"""
|
|
self._editMode = mode
|
|
|
|
def getEditMode(self):
|
|
"""Get current neighborhood editing mode"""
|
|
return self._editMode
|
|
|
|
class LevelAttribute:
|
|
"""Class which encapsulates a pie menu and a set of items"""
|
|
def __init__(self, name):
|
|
# Record name
|
|
self.name = name
|
|
# Pie menu used to pick an option
|
|
self._menu = None
|
|
# Dictionary of available options
|
|
self._dict = {}
|
|
self._list = []
|
|
self.count = 0
|
|
# Currently selected option
|
|
self._current = None
|
|
def setCurrent(self, newValue, fEvent = 1):
|
|
self._current = newValue
|
|
# Send event if specified
|
|
if fEvent:
|
|
messenger.send('select_' + self.name, [self._current])
|
|
def setMenu(self, menu):
|
|
self._menu = menu
|
|
self._menu.action = self.setCurrent
|
|
def setDict(self, dict):
|
|
self._dict = dict
|
|
# Create a list from the dictionary
|
|
self._list = dict.values()
|
|
# Update count
|
|
self.count = len(self._list)
|
|
# Initialize current to first item
|
|
if (len(self._list) > 0):
|
|
self._current = self._list[0]
|
|
def setList(self, list):
|
|
self._list = list
|
|
# Create a dictionary from the list
|
|
self._dict = {}
|
|
self.count = 0
|
|
for item in list:
|
|
self._dict[self.count] = item
|
|
self.count += 1
|
|
# Initialize current to first item
|
|
if (self.count > 0):
|
|
self._current = self._list[0]
|
|
def add(self, item):
|
|
self._dict[self.count] = item
|
|
self._list.append(item)
|
|
self.count += 1
|
|
def getCurrent(self):
|
|
return self._current
|
|
def getMenu(self):
|
|
return self._menu
|
|
def getDict(self):
|
|
return self._dict
|
|
def getList(self):
|
|
return self._list
|
|
def getCount(self):
|
|
return self.count
|
|
|
|
class DNAFlatBuildingStyle:
|
|
"""Class to hold attributes of a building style"""
|
|
def __init__(self, building = None, styleList = None, name = 'bldg_style'):
|
|
self.name = name
|
|
if building:
|
|
# Passed in a building
|
|
self.copy(building)
|
|
elif styleList != None:
|
|
# Passed in a list of style-height pairs
|
|
self.styleList = []
|
|
self.heightList = []
|
|
self.numWalls = 0
|
|
for pair in styleList:
|
|
self.add(pair[0], pair[1])
|
|
else:
|
|
# Use default style/height
|
|
self.styleList = [DNAWallStyle()]
|
|
self.heightList = [10]
|
|
self.numWalls = 1
|
|
|
|
def add(self, style, height):
|
|
self.styleList.append(style)
|
|
self.heightList.append(height)
|
|
self.numWalls += 1
|
|
|
|
def copy(self, building):
|
|
self.styleList = []
|
|
self.heightList = DNAGetWallHeights(building)[0]
|
|
self.numWalls = 0
|
|
for i in range(building.getNumChildren()):
|
|
child = building.at(i)
|
|
if DNAClassEqual(child, DNA_WALL):
|
|
wallStyle = DNAWallStyle(wall = child)
|
|
self.styleList.append(wallStyle)
|
|
self.numWalls += 1
|
|
|
|
def output(self, file = sys.stdout):
|
|
file.write('buildingStyle: %s\n' % createHeightCode(self.heightList))
|
|
for style in self.styleList:
|
|
style.output(file)
|
|
file.write('endBuildingStyle\n')
|
|
|
|
def createHeightCode(heightList):
|
|
def joinHeights(h1, h2):
|
|
return '%s_%s' % (h1, h2)
|
|
hl = map(ROUND_INT, heightList)
|
|
if len(hl) == 1:
|
|
return `hl[0]`
|
|
return reduce(joinHeights, hl)
|
|
|
|
def calcHeight(heightList):
|
|
height = 0
|
|
for h in heightList:
|
|
height = height + h
|
|
return int(height)
|
|
|
|
class DNABaselineStyle:
|
|
"""Class to hold attributes of a baseline style (wiggle, colors, etc)"""
|
|
def __init__(self, baseline = None, name = 'baseline_style'):
|
|
# First initialize everything
|
|
self.name = name
|
|
self.code = None # i.e. font
|
|
self.kern = None
|
|
self.wiggle = None
|
|
self.stumble = None
|
|
self.stomp = None
|
|
self.curve = None
|
|
self.x = None
|
|
self.z = None
|
|
self.scaleX = 1.0
|
|
self.scaleZ = 1.0
|
|
self.roll = None
|
|
self.flags = None
|
|
self.color = Vec4(1.0)
|
|
# Then copy the specifics for the baseline if input
|
|
if baseline:
|
|
self.copy(baseline)
|
|
|
|
def copy(self, baseline):
|
|
self.code = baseline.getCode()
|
|
self.kern = baseline.getKern()
|
|
self.wiggle = baseline.getWiggle()
|
|
self.stumble = baseline.getStumble()
|
|
self.stomp = baseline.getStomp()
|
|
width=baseline.getWidth()
|
|
if width:
|
|
self.curve = 1.0/width
|
|
else:
|
|
self.curve = 0.0
|
|
pos = baseline.getPos()
|
|
self.x = pos[0]
|
|
self.z = pos[2]
|
|
scale = baseline.getScale()
|
|
self.scaleX = scale[0]
|
|
self.scaleZ = scale[2]
|
|
hpr = baseline.getHpr()
|
|
self.roll = hpr[2]
|
|
self.flags = baseline.getFlags()
|
|
self.color = baseline.getColor()
|
|
|
|
def copyTo(self, baseline):
|
|
if self.code != None:
|
|
baseline.setCode(self.code)
|
|
if self.kern != None:
|
|
baseline.setKern(self.kern)
|
|
if self.wiggle != None:
|
|
baseline.setWiggle(self.wiggle)
|
|
if self.stumble != None:
|
|
baseline.setStumble(self.stumble)
|
|
if self.stomp != None:
|
|
baseline.setStomp(self.stomp)
|
|
if self.curve != None:
|
|
diameter=0.0
|
|
if self.curve:
|
|
diameter=1.0/self.curve
|
|
baseline.setWidth(diameter)
|
|
baseline.setHeight(diameter)
|
|
if (self.x != None) or (self.z != None):
|
|
pos=baseline.getPos()
|
|
if self.x != None:
|
|
pos=Vec3(self.x, pos[1], pos[2])
|
|
if self.z != None:
|
|
pos=Vec3(pos[0], pos[1], self.z)
|
|
baseline.setPos(pos)
|
|
if self.roll != None:
|
|
baseline.setHpr(Vec3(0.0, 0.0, self.roll))
|
|
if (self.scaleX != None) or (self.scaleZ != None):
|
|
scale=baseline.getScale()
|
|
if self.scaleX != None:
|
|
scale=Vec3(self.scaleX, scale[1], scale[2])
|
|
if self.scaleZ != None:
|
|
scale=Vec3(scale[0], scale[1], self.scaleZ)
|
|
baseline.setScale(scale)
|
|
if self.flags != None:
|
|
baseline.setFlags(self.flags)
|
|
if self.color != None:
|
|
baseline.setColor(self.color)
|
|
|
|
def output(self, file = sys.stdout):
|
|
""" Output style description to a file """
|
|
file.write('baselineStyle\n')
|
|
if self.name != None:
|
|
file.write('name: %s\n' % self.name)
|
|
if self.code != None:
|
|
file.write('code: %s\n' % self.code)
|
|
if self.kern != None:
|
|
file.write('kern: %s\n' % self.kern)
|
|
if self.wiggle != None:
|
|
file.write('wiggle: %s\n' % self.wiggle)
|
|
if self.stumble != None:
|
|
file.write('stumble: %s\n' % self.stumble)
|
|
if self.stomp != None:
|
|
file.write('stomp: %s\n' % self.stomp)
|
|
if self.curve != None:
|
|
file.write('curve: %s\n' % self.curve)
|
|
if self.x != None:
|
|
file.write('x: %s\n' % self.x)
|
|
if self.z != None:
|
|
file.write('z: %s\n' % self.z)
|
|
if self.scaleX != None:
|
|
file.write('scaleX: %s\n' % self.scaleX)
|
|
if self.scaleZ != None:
|
|
file.write('scaleZ: %s\n' % self.scaleZ)
|
|
if self.roll != None:
|
|
file.write('roll: %s\n' % self.roll)
|
|
if self.flags != None:
|
|
file.write('flags: %s\n' % self.flags)
|
|
if self.color != None:
|
|
file.write('color: Vec4(%.2f, %.2f, %.2f, 1.0)\n'
|
|
% (self.color[0], self.color[1], self.color[2]))
|
|
file.write('endBaselineStyle\n')
|
|
|
|
# Convience functions to facilitate class use
|
|
def __setitem__(self, index, item):
|
|
self.__dict__[index] = item
|
|
|
|
def __getitem__(self, index):
|
|
return self.__dict__[index]
|
|
|
|
def __repr__(self):
|
|
return(
|
|
'name: %s\n' % self.name +
|
|
'code: %s\n' % self.code +
|
|
'kern: %s\n' % self.kern +
|
|
'wiggle: %s\n' % self.wiggle +
|
|
'stumble: %s\n' % self.stumble +
|
|
'stomp: %s\n' % self.stomp +
|
|
'curve: %s\n' % self.curve +
|
|
'x: %s\n' % self.x +
|
|
'z: %s\n' % self.z +
|
|
'scaleX: %s\n' % self.scaleX +
|
|
'scaleZ: %s\n' % self.scaleZ +
|
|
'roll: %s\n' % self.roll +
|
|
'flags: %s\n' % self.flags +
|
|
'color: %s\n' % self.color
|
|
)
|
|
|
|
class DNAWallStyle:
|
|
"""Class to hold attributes of a wall style (textures, colors, etc)"""
|
|
def __init__(self, wall = None, name = 'wall_style'):
|
|
# First initialize everything
|
|
self.name = name
|
|
self.wall_texture = 'wall_md_blank_ur'
|
|
self.wall_color = Vec4(1.0)
|
|
self.window_count = 2
|
|
self.window_texture = None
|
|
self.window_color = Vec4(1.0)
|
|
self.window_awning_texture = None
|
|
self.window_awning_color = Vec4(1.0)
|
|
self.door_texture = None
|
|
self.door_color = Vec4(1.0)
|
|
self.door_awning_texture = None
|
|
self.door_awning_color = Vec4(1.0)
|
|
self.cornice_texture = None
|
|
self.cornice_color = Vec4(1.0)
|
|
# Then copy the specifics for the wall if input
|
|
if wall:
|
|
self.copy(wall)
|
|
|
|
def copy(self, wall):
|
|
self.wall_texture = wall.getCode()
|
|
self.wall_color = wall.getColor()
|
|
for i in range(wall.getNumChildren()):
|
|
child = wall.at(i)
|
|
if DNAClassEqual(child, DNA_WINDOWS):
|
|
self.window_count = child.getWindowCount()
|
|
self.window_texture = child.getCode()
|
|
self.window_color = child.getColor()
|
|
# MRM: Check for awnings here
|
|
elif DNAClassEqual(child, DNA_DOOR):
|
|
self.door_texture = child.getCode()
|
|
self.door_color = child.getColor()
|
|
elif DNAClassEqual(child, DNA_FLAT_DOOR):
|
|
self.door_texture = child.getCode()
|
|
self.door_color = child.getColor()
|
|
# MRM: Check for awnings here
|
|
elif DNAClassEqual(child, DNA_CORNICE):
|
|
self.cornice_texture = child.getCode()
|
|
self.cornice_color = child.getColor()
|
|
|
|
def output(self, file = sys.stdout):
|
|
""" Output style description to a file """
|
|
def writeAttributes(f, type, s = self):
|
|
color = s[type + '_color']
|
|
f.write('%s_texture: %s\n' % (type, s[type + '_texture']))
|
|
f.write('%s_color: Vec4(%.2f, %.2f, %.2f, 1.0)\n' %
|
|
(type, color[0], color[1], color[2]))
|
|
file.write('wallStyle\n')
|
|
writeAttributes(file, 'wall')
|
|
if self['window_texture']:
|
|
writeAttributes(file, 'window')
|
|
file.write('window_count: %s\n' % self['window_count'])
|
|
if self['window_awning_texture']:
|
|
writeAttributes(file, 'window_awning')
|
|
if self['door_texture']:
|
|
writeAttributes(file, 'door')
|
|
if self['door_awning_texture']:
|
|
writeAttributes(file, 'door_awning')
|
|
if self['cornice_texture']:
|
|
writeAttributes(file, 'cornice')
|
|
file.write('endWallStyle\n')
|
|
|
|
# Convience functions to facilitate class use
|
|
def __setitem__(self, index, item):
|
|
self.__dict__[index] = item
|
|
|
|
def __getitem__(self, index):
|
|
return self.__dict__[index]
|
|
|
|
def __repr__(self):
|
|
return(
|
|
'Name: %s\n' % self.name +
|
|
'Wall Texture: %s\n' % self.wall_texture +
|
|
'Wall Color: %s\n' % self.wall_color +
|
|
'Window Texture: %s\n' % self.window_texture +
|
|
'Window Color: %s\n' % self.window_color +
|
|
'Window Awning Texture: %s\n' % self.window_awning_texture +
|
|
'Window Awning Color: %s\n' % self.window_awning_color +
|
|
'Door Texture: %s\n' % self.door_texture +
|
|
'Door Color: %s\n' % self.door_color +
|
|
'Door Awning Texture: %s\n' % self.door_awning_texture +
|
|
'Door Awning Color: %s\n' % self.door_awning_color +
|
|
'Cornice Texture: %s\n' % self.cornice_texture +
|
|
'Cornice Color: %s\n' % self.cornice_color
|
|
)
|