Exemplo n.º 1
0
Arquivo: udkParser.py Projeto: m2u/m2u
def parseActor(unrtext, safe=False):
    """ parse UnrealText of a single Actor into an ObjectInfo representation.

    :param unrtext: the unreal text for the level
    :param safe: the first line in unrtext is the Begin Actor line
    and the End Actor line is not present
    
    :return: instance of :class:`m2u.helper.ObjectInfo.ObjectInfo`

    .. note: if you provide text with more than one Actor in it, only the first
    Actor will be converted.
    If you have a multi-selection, use :func:`parseActors`
    
    """
    # to keep it simple we currently only get the entries we are interested
    # in and pile everything else up to a text, so we do no sub-object parsing
    # this may change in the future!
    
    sindex = 0
    # split every line and erase leading whitespaces, removes empty lines
    lines = re.split("\n+\s*", unrtext)
    # find the first line that begins the Actor (most likely the first line)
    if not safe: # no preprocessing was done, most likely the third line then
        for i in range(len(lines)):
            if lines[i].startswith("Begin Actor"):
                sindex = i
                break
    g = re.search("Class=(.+?) Name=(.+?)\s+", lines[sindex])
    if not g: # no name? invalid text, obviously
        _lg.error( "no name and type found for object")
        return None
    objtype = g.group(1)
    objname = g.group(2)
    objtypecommon = getCommonTypeFromInternal(objtype)
    objInfo = ObjectInfo(objname, objtype, objtypecommon)
    textblock = ""
    for line in lines[sindex+1:]:
        # add jumping over sub-object groups (skip lines inbetween or so)
        # if line startswith "Begin Object"
        # dumb copy lines until
        # line startswith "End Object" is found
        # keep track of depth (begin obj,begin obj, end obj ->)
        if not safe and line.startswith("End Actor"):
            break # done reading actor
        elif line.startswith("Location="):
            objInfo.position = _getFloatTuple(line)
        elif line.startswith("Rotation="):
            rot = _getFloatTuple(line)
            objInfo.rotation = _convertRotationFromUDK(rot)
        elif line.startswith("DrawScale3D="):
            objInfo.scale = _getFloatTuple(line)
        else:
            textblock += ("\n"+line)
    objInfo.attrs["textblock"]=textblock
    return objInfo
Exemplo n.º 2
0
def parseActor(unrtext, safe=False):
    """ parse UnrealText of a single Actor into an ObjectInfo representation.

    :param unrtext: the unreal text for the level
    :param safe: the first line in unrtext is the Begin Actor line
    and the End Actor line is not present
    
    :return: instance of :class:`m2u.helper.ObjectInfo.ObjectInfo`

    .. note: if you provide text with more than one Actor in it, only the first
    Actor will be converted.
    If you have a multi-selection, use :func:`parseActors`
    
    """
    # to keep it simple we currently only get the entries we are interested
    # in and pile everything else up to a text, so we do no sub-object parsing
    # this may change in the future!

    sindex = 0
    # split every line and erase leading whitespaces, removes empty lines
    lines = re.split("\n+\s*", unrtext)
    # find the first line that begins the Actor (most likely the first line)
    if not safe:  # no preprocessing was done, most likely the third line then
        for i in range(len(lines)):
            if lines[i].startswith("Begin Actor"):
                sindex = i
                break
    g = re.search("Class=(.+?) Name=(.+?)\s+", lines[sindex])
    if not g:  # no name? invalid text, obviously
        _lg.error("no name and type found for object")
        return None
    objtype = g.group(1)
    objname = g.group(2)
    objtypecommon = getCommonTypeFromInternal(objtype)
    objInfo = ObjectInfo(objname, objtype, objtypecommon)
    textblock = ""
    for line in lines[sindex + 1:]:
        # add jumping over sub-object groups (skip lines inbetween or so)
        # if line startswith "Begin Object"
        # dumb copy lines until
        # line startswith "End Object" is found
        # keep track of depth (begin obj,begin obj, end obj ->)
        if not safe and line.startswith("End Actor"):
            break  # done reading actor
        elif line.startswith("Location="):
            objInfo.position = _getFloatTuple(line)
        elif line.startswith("Rotation="):
            rot = _getFloatTuple(line)
            objInfo.rotation = _convertRotationFromUDK(rot)
        elif line.startswith("DrawScale3D="):
            objInfo.scale = _getFloatTuple(line)
        else:
            textblock += ("\n" + line)
    objInfo.attrs["textblock"] = textblock
    return objInfo
Exemplo n.º 3
0
def sendSelectedToEd():
    """
    there is the special case where there is one type of mesh in the scene
    with edited geometry but an AssetPath pointing to the unedited file
    if the user wants that object to be exported as a new asset and don't
    overwrite the existing asset, (our automation couldn't detect that, because
    all assets with same path have same geometry)
    the user would have to call sendSelectedAsNew in that case?
    Then we would make sure that the user is provided with the UI to change the
    assetPath of those objects on export.
    If the user does not choose sendAsNew, we assume he wants to overwrite any
    files on disk for sure.
    There is also the case where we don't want to export objects that are already
    in the editor, because reimporting takes time, if we didn't change the asset,
    don't ask. Also, we maybe don't want to overwrite files that already are on
    disk, because that takes time.
    We only want to create the map from the objects we have and add those objects
    that we don't have.
    So we need a third option "sendSelectedAddMissingOnly"
    We might intelligently do a check on file-date, import-date in editor
    and maybe a import-date in program for one or the other case to determine
    if a reimport or a file overwrite is necessary or not

    What is the "AssetPath" supposed to be?
    It is the file path, including the file-extension, relative to the current
    projects Art-Source folder. No absolute paths, but that is depending on the
    actual pipeline-implementation, since all functions that deal with
    file paths will be delegated to a pipeline module, and that may be replaced
    by the user.
    
    1. get selected objects
    2. for each object, get the "AssetPath" attribute
    3. if the object has no such attribute, save it in a untagged list
       if it has, save it in a tagged list entry (filepath:[objects])
    4. for each object in the tagged list, check if the file exists on disk
       fancy: check each object with same file if the gemometry is same
       - if not, tell user that objects with same file path differ and that he should
       export as new.
       - do that by adding all objects with same geometry to a list
       - then ask the user how to rename that asset
       - replace the assetPath value on all assets in that list and add
         them to the tagged unique list
    5. if it does not, export one of the associated objects as asset
    (overwrite any file found)
    6. for each object in the untagged list
       fancy: check all other objects in the untagged list if the geometry is same
       for each where it is same, insert in an association list
       (firstObject:[matchingObjects])
       fancy2: ask the user if the "unique" objects as found are ok,
               if not, he has to split objects into a new "unique" group
       for each first object in that association list
       check if the geometry is the same as in any of the tagged list uniques
       if it is, set the AssetPath attrib on all matching objects to that of the unique
       if it is not, export it as a new Asset and set the AssetPath attrib on
       all matching objects
       add all those to the tagged association list
    7. for each unique object in the tagged list
       tell the editor to import the associated file
    8. for each selected object
       tell the editor to add an actor with the AssetPath as asset
    extend to send lights and stuff like that

    this function will perform something between O(n) and O(n^2) would need to analyze this a little more
    only counting objects... vertex count may be different in scenes, and the heavier
    the vert count, again the heavier the check will be...
    """

    # 1. get selected objects (only transform nodes)
    selectedObjects = pm.selected(type="transform")
    # filter out transforms that don't have a mesh-shape node
    selectedMeshes = list()
    for obj in selectedObjects:
        meshShapes = pm.listRelatives(obj, shapes=True, type="mesh")
        if len(meshShapes) > 0:
            selectedMeshes.append(obj)
    _lg.debug("found %i selected meshes" % len(selectedMeshes))
    # TODO: maybe filter other transferable stuff like lights or so

    # 2. for each object get the "AssetPath" attribute
    untaggedList = list()
    taggedDict = {}
    for obj in selectedMeshes:
        if obj.hasAttr("AssetPath"):
            assetPathAttr = obj.attr("AssetPath")
            assetPathValue = assetPathAttr.get()
            if len(assetPathValue) > 0:
                taggedDict.setdefault(assetPathValue, []).append(obj)
            else:
                # if the ass path is empty, that is equal to the attr not being there
                untaggedList.append(obj)
        else:
            # unknown asset, we will handle those later
            untaggedList.append(obj)
    _lg.debug("found %i untagged" % len(untaggedList))
    _lg.debug("found %i tagged" % len(taggedDict))

    # 3. do the geometry check for tagged objects
    #   this assembles the taggedUniqueDict
    taggedDiscrepancyDetected = False
    taggedUniqueDict = {}
    for lis in taggedDict.values():
        # for obj in lis: #we modify lis, so iterator won't work
        while len(lis) > 0:  # use while instead
            obj = lis[0]
            taggedUniqueDict[obj] = []
            # compare this object against all others in the list.
            for otherObj in lis[1:]:
                if 0 == pm.polyCompare(obj, otherObj, vertices=True):
                    # if the geometry matches, add the other to the unique list
                    # with this object as key, and remove from the old list
                    taggedUniqueDict[obj].append(otherObj)
                    lis.remove(otherObj)
                else:
                    taggedDiscrepancyDetected = True
            lis.remove(obj)  # we are done with this object too
    _lg.debug("found %i tagged uniques" % len(taggedUniqueDict))

    # 3. do the geometry check for untagged objects
    untaggedUniquesDetected = False
    while len(untaggedList) > 0:
        obj = untaggedList[0]
        if not obj.hasAttr("AssetPath"):
            pm.addAttr(obj.name(), longName="AssetPath", dataType="string", keyable=False)
        foundUniqueForMe = False
        # compare against one of the tagged uniques
        for other in taggedUniqueDict.keys():
            # if that geometry matches, we found the unique for this obj
            if 0 == pm.polyCompare(obj, other, vertices=True):
                taggedUniqueDict[other].append(obj)
                # set "AssetPath" attr to match that of the unique
                obj.attr("AssetPath").set(other.attr("AssetPath").get())
                # we are done with this object
                untaggedList.remove(obj)
                foundUniqueForMe = True
                _lg.debug("found a unique key (%s) for %s" % (other.name(), obj.name()))
                break

        if not foundUniqueForMe:
            untaggedUniquesDetected = True
            # make this a new unique, simply take the objects name as AssetPath
            npath = obj.shortName() + "_AutoExport" + ".fbx"
            obj.attr("AssetPath").set(npath)
            taggedUniqueDict[obj] = []
            untaggedList.remove(obj)
            _lg.debug("assuming new untagged unique: " + obj.shortName())
            # we will automatically compare to all other untagged to find
            # members for our new unique in the next loop iteration
    _lg.debug("found %i uniques (with untagged)" % len(taggedUniqueDict))

    # TODO: 4. UI-stuff...
    # 4. if taggedDiscrepancy or untaggedUniques were detected,
    # list all uniques in the UI and let the user change names
    # force him to change names for taggedUniques with same AssetPath of course
    # if taggedDiscrepancyDetected:
    # for unique in taggedUniqueDict.keys():
    # it is up to the UI to do that and let the user
    # set a new assetPath on any of those unique guy's lists

    # 5. export files stuff
    for obj in taggedUniqueDict.keys():
        exportObjectAsAsset(obj.name(), obj.attr("AssetPath").get())

    # 6. tell the editor to import all the uniques
    fileList = []
    for obj in taggedUniqueDict.keys():
        fileList.append(obj.attr("AssetPath").get())
    m2u.core.getEditor().importAssetsBatch(fileList)

    # 7. tell the editor to assemble the scene
    objInfoList = []
    for obj in selectedMeshes:
        # TODO: make that a new function
        objInfo = ObjectInfo(name=obj.shortName(), typeInternal="mesh", typeCommon="mesh")
        objTransforms = m2u.maya.mayaObjectTracker.getTransformationFromObj(obj)
        objInfo.pos = objTransforms[0]
        objInfo.rot = objTransforms[1]
        objInfo.scale = objTransforms[2]
        objInfo.AssetPath = obj.attr("AssetPath").get()
        objInfoList.append(objInfo)
    m2u.core.getEditor().addActorBatch(objInfoList)
Exemplo n.º 4
0
def sendSelectedToEd():
    """
    there is the special case where there is one type of mesh in the scene
    with edited geometry but an AssetPath pointing to the unedited file
    if the user wants that object to be exported as a new asset and don't
    overwrite the existing asset, (our automation couldn't detect that, because
    all assets with same path have same geometry)
    the user would have to call sendSelectedAsNew in that case?
    Then we would make sure that the user is provided with the UI to change the
    assetPath of those objects on export.
    If the user does not choose sendAsNew, we assume he wants to overwrite any
    files on disk for sure.
    There is also the case where we don't want to export objects that are already
    in the editor, because reimporting takes time, if we didn't change the asset,
    don't ask. Also, we maybe don't want to overwrite files that already are on
    disk, because that takes time.
    We only want to create the map from the objects we have and add those objects
    that we don't have.
    So we need a third option "sendSelectedAddMissingOnly"
    We might intelligently do a check on file-date, import-date in editor
    and maybe a import-date in program for one or the other case to determine
    if a reimport or a file overwrite is necessary or not

    What is the "AssetPath" supposed to be?
    It is the file path, including the file-extension, relative to the current
    projects Art-Source folder. No absolute paths, but that is depending on the
    actual pipeline-implementation, since all functions that deal with
    file paths will be delegated to a pipeline module, and that may be replaced
    by the user.
    
    1. get selected objects
    2. for each object, get the "AssetPath" attribute
    3. if the object has no such attribute, save it in a untagged list
       if it has, save it in a tagged list entry (filepath:[objects])
    4. for each object in the tagged list, check if the file exists on disk
       fancy: check each object with same file if the gemometry is same
       - if not, tell user that objects with same file path differ and that he should
       export as new.
       - do that by adding all objects with same geometry to a list
       - then ask the user how to rename that asset
       - replace the assetPath value on all assets in that list and add
         them to the tagged unique list
    5. if it does not, export one of the associated objects as asset
    (overwrite any file found)
    6. for each object in the untagged list
       fancy: check all other objects in the untagged list if the geometry is same
       for each where it is same, insert in an association list
       (firstObject:[matchingObjects])
       fancy2: ask the user if the "unique" objects as found are ok,
               if not, he has to split objects into a new "unique" group
       for each first object in that association list
       check if the geometry is the same as in any of the tagged list uniques
       if it is, set the AssetPath attrib on all matching objects to that of the unique
       if it is not, export it as a new Asset and set the AssetPath attrib on
       all matching objects
       add all those to the tagged association list
    7. for each unique object in the tagged list
       tell the editor to import the associated file
    8. for each selected object
       tell the editor to add an actor with the AssetPath as asset
    extend to send lights and stuff like that

    this function will perform something between O(n) and O(n^2) would need to analyze this a little more
    only counting objects... vertex count may be different in scenes, and the heavier
    the vert count, again the heavier the check will be...
    """

    #1. get selected objects (only transform nodes)
    selectedObjects = pm.selected(type="transform")
    # filter out transforms that don't have a mesh-shape node
    selectedMeshes = list()
    for obj in selectedObjects:
        meshShapes = pm.listRelatives(obj, shapes=True, type="mesh")
        if len(meshShapes)>0:
            selectedMeshes.append(obj)
    _lg.debug("found %i selected meshes" % len(selectedMeshes))
    # TODO: maybe filter other transferable stuff like lights or so
    
    #2. for each object get the "AssetPath" attribute
    untaggedList = list()
    taggedDict = {}
    for obj in selectedMeshes:
        if obj.hasAttr("AssetPath"):
            assetPathAttr = obj.attr("AssetPath")
            assetPathValue = assetPathAttr.get()
            if len(assetPathValue)>0:
                taggedDict.setdefault(assetPathValue,[]).append(obj)
            else:
                # if the ass path is empty, that is equal to the attr not being there
                untaggedList.append(obj)
        else:
            # unknown asset, we will handle those later
            untaggedList.append(obj)
    _lg.debug("found %i untagged" % len(untaggedList))
    _lg.debug("found %i tagged" % len(taggedDict))
    
    #3. do the geometry check for tagged objects
    #   this assembles the taggedUniqueDict
    taggedDiscrepancyDetected = False
    taggedUniqueDict = {}
    for lis in taggedDict.values():
        #for obj in lis: #we modify lis, so iterator won't work
        while len(lis)>0: # use while instead
            obj = lis[0]
            taggedUniqueDict[obj]=[]
            # compare this object against all others in the list.
            for otherObj in lis[1:]:
                if 0 == pm.polyCompare(obj, otherObj, vertices=True):
                    # if the geometry matches, add the other to the unique list
                    # with this object as key, and remove from the old list
                    taggedUniqueDict[obj].append(otherObj)
                    lis.remove(otherObj)
                else:
                    taggedDiscrepancyDetected = True
            lis.remove(obj) # we are done with this object too
    _lg.debug("found %i tagged uniques" % len(taggedUniqueDict))
    
    #3. do the geometry check for untagged objects
    untaggedUniquesDetected = False
    while len(untaggedList)>0:
        obj = untaggedList[0]
        if not obj.hasAttr("AssetPath"):
            pm.addAttr(obj.name(), longName="AssetPath", dataType="string",
                       keyable=False)
        foundUniqueForMe = False
        # compare against one of the tagged uniques
        for other in taggedUniqueDict.keys():
            # if that geometry matches, we found the unique for this obj
            if 0 == pm.polyCompare(obj, other, vertices=True):
                taggedUniqueDict[other].append(obj)
                # set "AssetPath" attr to match that of the unique
                obj.attr("AssetPath").set(other.attr("AssetPath").get())
                # we are done with this object
                untaggedList.remove(obj)
                foundUniqueForMe = True
                _lg.debug("found a unique key (%s) for %s" %(other.name(), obj.name()))
                break
        
        if not foundUniqueForMe:
            untaggedUniquesDetected = True
            # make this a new unique, simply take the objects name as AssetPath
            npath = obj.shortName() + "_AutoExport" + ".fbx"
            obj.attr("AssetPath").set(npath)
            taggedUniqueDict[obj]=[]
            untaggedList.remove(obj)
            _lg.debug("assuming new untagged unique: "+obj.shortName())
            # we will automatically compare to all other untagged to find
            # members for our new unique in the next loop iteration
    _lg.debug("found %i uniques (with untagged)" % len(taggedUniqueDict))
    
    # TODO: 4. UI-stuff...
    #4. if taggedDiscrepancy or untaggedUniques were detected,
    # list all uniques in the UI and let the user change names
    # force him to change names for taggedUniques with same AssetPath of course
    # if taggedDiscrepancyDetected:
        #for unique in taggedUniqueDict.keys():
            # it is up to the UI to do that and let the user
            # set a new assetPath on any of those unique guy's lists
    
    #5. export files stuff
    for obj in taggedUniqueDict.keys():
        exportObjectAsAsset(obj.name(), obj.attr("AssetPath").get())
        
    #6. tell the editor to import all the uniques
    fileList = []
    for obj in taggedUniqueDict.keys():
        fileList.append(obj.attr("AssetPath").get())
    m2u.core.getEditor().importAssetsBatch(fileList)
        
    #7. tell the editor to assemble the scene
    objInfoList = []
    for obj in selectedMeshes:
        # TODO: make that a new function
        objInfo = ObjectInfo(name = obj.shortName(), typeInternal = "mesh",
                             typeCommon = "mesh")
        objTransforms = m2u.maya.mayaObjectTracker.getTransformationFromObj(obj)
        objInfo.pos = objTransforms[0]
        objInfo.rot = objTransforms[1]
        objInfo.scale = objTransforms[2]
        objInfo.AssetPath = obj.attr("AssetPath").get()
        objInfoList.append(objInfo)
    m2u.core.getEditor().addActorBatch(objInfoList)