From 9c9d3d0707e283ed5e73caf58166e6f75ac71950 Mon Sep 17 00:00:00 2001 From: Gyedo Jeon Date: Wed, 3 Sep 2008 20:34:47 +0000 Subject: [PATCH] Sepearated LevelStyleManager for RTM --- direct/src/leveleditor/LevelEditor.py | 1412 +---------------- direct/src/leveleditor/LevelStyleManager.py | 1558 +++++++++++++++++++ 2 files changed, 1561 insertions(+), 1409 deletions(-) create mode 100755 direct/src/leveleditor/LevelStyleManager.py diff --git a/direct/src/leveleditor/LevelEditor.py b/direct/src/leveleditor/LevelEditor.py index 17377d033b..c4565fc81e 100644 --- a/direct/src/leveleditor/LevelEditor.py +++ b/direct/src/leveleditor/LevelEditor.py @@ -37,6 +37,8 @@ from otp.avatar import LocalAvatar from toontown.toon import RobotToon from otp.otpbase import OTPGlobals +from LevelStyleManager import * + # Force direct and tk to be on base.startDirect(fWantDirect = 1, fWantTk = 1) @@ -287,159 +289,6 @@ except NameError: loadDNAFile(DNASTORE, 'phase_12/dna/storage_CC_sz.dna', CSDefault, 1) __builtin__.dnaLoaded = 1 -# 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 LevelEditor(NodePath, DirectObject): """Class used to create a Toontown LevelEditor object""" @@ -477,7 +326,7 @@ class LevelEditor(NodePath, DirectObject): # Create ancillary objects # Style manager for keeping track of styles/colors - self.styleManager = LevelStyleManager() + self.styleManager = LevelStyleManager(NEIGHBORHOODS, NEIGHBORHOOD_CODES) # Load neighborhood maps self.createLevelMaps() # Marker for showing next insertion point @@ -4194,1261 +4043,6 @@ class LevelEditor(NodePath, DirectObject): else: return self.findBldgEndPoint(bldgWidth, curve, currT, currPoint, startT = midT, endT = endT, rd = rd + 1) -class LevelStyleManager: - """Class which reads in style files and manages class variables""" - def __init__(self): - # 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 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 = 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 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 = 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 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 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 = 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 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 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)) - 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) - - # 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 - ) class OldLevelEditor(NodePath, DirectObject): pass diff --git a/direct/src/leveleditor/LevelStyleManager.py b/direct/src/leveleditor/LevelStyleManager.py new file mode 100755 index 0000000000..3f8a883b4a --- /dev/null +++ b/direct/src/leveleditor/LevelStyleManager.py @@ -0,0 +1,1558 @@ +import sys, string, math, types +from pandac.PandaModules import * +import direct.gui.DirectGuiGlobals as DGG + +from PieMenu 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)) + 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) + + # 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 + )