"""
HB_StartRetopo V1.1

Last Modified: 1/1/2016
Works with CINEMA 4D R16.050 and up.
Copyright: Holger Biebrach, www.c4dstuff.com

Name-US: HB_StartRetopo/SlideMode
Description-US: Start Retopo for selected object (select Highres and Lowres to resume retopo) [CTRL: Use Slide Mode]

Usage:
Select any Kind of Polygonobject. If you run the Script you will be asked if the Object is symmetrical. Press yes to setup a symmetry object. 
If you select the lowres and the highres mesh the script will setup everything to modify existing topology. If you press CTRL and run the Script
you will enter Slidemode which allows you to slide Polygons/Edges/Points on the Surface of the Object. Use the ReProject Script to get out of this mode.
It will convert all to Polygons.

Video Tutorials:
https://youtu.be/Ki6I9sRdeUU?t=49m24s
https://youtu.be/Xo1qMmVSQ-Q?t=7s
https://youtu.be/ZDkXn5PjsjU

ChangeLog:

1/1/2016 v1.1
- Python Rewrite
- Polysnapping will be enabled
- DisplayTag on RetopoObject
- Displaycolor
- Support for resuming retopo by selecting two meshes
- SlideMode for componentsliding

6/11/2015 v1.0 
Release Version

"""

import c4d
from c4d import gui, plugins, utils
from c4d.modules import snap



def deletehalf(obj):

    sel = obj.GetAllPoints()
    pnts = c4d.BaseSelect()
    pnts.DeselectAll()
    points = [(i, point)
          for i, point in enumerate(obj.GetAllPoints())
          ]

    if not points:
      return
    sel = obj.GetPointS()
    sel.DeselectAll()
    
    for i, point in points:
      pos=obj.GetPoint(i)
      xpos=pos.x

      
      
      if xpos > 0:

        sel.Select(i)
    
    
    settings = c4d.BaseContainer()                 # Settings

    
    res = utils.SendModelingCommand(command = c4d.MCOMMAND_DELETE,
                                    list = [obj],
                                    mode = c4d.MODELINGCOMMANDMODE_POINTSELECTION,
                                    bc = settings,
                                    doc = doc)


def tool():
    return plugins.FindPlugin(doc.GetAction(), c4d.PLUGINTYPE_TOOL)


def retopomeshColor():
    r = 150 / 256.0    
    g = 150 / 256.0    
    b = 150 / 256.0 
    color = c4d.Vector(r,g,b)
    return color

def selobjColor():
    r = 70 / 256.0    
    g = 80 / 256.0    
    b = 120 / 256.0 
    color = c4d.Vector(r,g,b)
    return color


def main():
    

    selobj=doc.GetSelection()
    
    if len(selobj)>2 or len(selobj)==0 : #ERROR
        
        gui.MessageDialog("Select one or two Objects!")
        return


    bc = c4d.BaseContainer()
    c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD,c4d.BFM_INPUT_CHANNEL,bc) #Modifier Key pressed?
    
    if bc[c4d.BFM_INPUT_QUALIFIER] ==2:
       CTRL=True
       if len(selobj)==2:
           gui.MessageDialog("Select one Object!")
           return
           
    else:
        CTRL=False



    if len(selobj)==2: #Retopo Resume Check which Object is Lowres
        PointCountA=selobj[0].GetPointCount()
        PointCountB=selobj[1].GetPointCount()
        
        
        if PointCountA > PointCountB:
            lowresobj = selobj[1]
            highresobj = selobj[0] 
        else:
            lowresobj = selobj[0]
            highresobj = selobj[1]  
        
        doc.StartUndo()
        
        
        
        
        
    else:
        highresobj=selobj[0]
        
    

    if CTRL == True and len(selobj)==1: #Slide Mode
        lowresobj=highresobj.GetClone()


    selobjname = highresobj.GetName()
    selobjpos = highresobj.GetMg()
    retoponame = selobjname + "_retopo"
    
    
    if CTRL == False:
        InputSymmetrical = gui.QuestionDialog("Is Mesh Symmetrical?")
    else:
        InputSymmetrical=False
    
   
    
    if len(selobj)==1 and CTRL == False:
        lowresobj=c4d.BaseObject(c4d.Opolygon)
        lowresobj.SetName(retoponame)

    if InputSymmetrical ==True:
        doc.AddUndo(c4d.UNDOTYPE_CHANGE, lowresobj)
        deletehalf(lowresobj)
        
    doc.AddUndo(c4d.UNDOTYPE_DELETE, lowresobj)
    lowresobj.Remove()

    
    if InputSymmetrical == True:                                ################# SYMMETRY
        
        symobj=c4d.BaseObject(c4d.Osymmetry)
        symobj[c4d.SYMMETRYOBJECT_TOLERANCE]=0.5
        symobj.SetMg(selobjpos)
        symobj.SetName(retoponame)
        c4dVersion= c4d.GetC4DVersion()
        if c4dVersion >= 17000:
            symobj[c4d.SYMMETRYOBJECT_CLAMPPOINTS]=True

        symobj.InsertBefore(highresobj)
        doc.AddUndo(c4d.UNDOTYPE_NEW, symobj)
        
        lowresobj.InsertUnder(symobj)
        doc.AddUndo(c4d.UNDOTYPE_NEW, lowresobj)

    else:                                                         # NO Symmetry
        
        lowresobj.InsertBefore(highresobj)
        doc.AddUndo(c4d.UNDOTYPE_NEW, lowresobj)
    

    
    #if len(selobj)==1: 
    doc.AddUndo(c4d.UNDOTYPE_CHANGE, lowresobj)  
    lowresobj.SetMg(selobjpos)
    
    #SHRINKWRAP
    shrinkwrapdeformer=c4d.BaseObject(1019774)
    shrinkwrapdeformer[c4d.SHRINKWRAP_TARGETOBJECT]=highresobj
    shrinkwrapdeformer[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR]=1
    
    shrinkwrapdeformer.InsertUnder(lowresobj)
    doc.AddUndo(c4d.UNDOTYPE_NEW, shrinkwrapdeformer)
 
 
 
 
    if CTRL == False:
        
        doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, shrinkwrapdeformer)
        shrinkwrapdeformer[c4d.ID_BASEOBJECT_GENERATOR_FLAG]=False
        doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, shrinkwrapdeformer)
        shrinkwrapdeformer.SetName("HB_RetopoProjector")
        
        tool()[c4d.MDATA_POLYPEN_QUADSTRIP]=True
        tool()[c4d.MDATA_POLYPEN_AUTOWELD]=True
        tool()[c4d.MDATA_POLYPEN_CREATENGONS]=True
        if not (c4d.IsCommandChecked(440000121)):
            c4d.CallCommand(440000121) # Snapping on
    
        c4d.CallCommand(c4d.SNAP_SETTINGS_AUTO)
                
        snap.EnableSnap(False, doc, c4d.SNAPMODE_POINT)
        snap.EnableSnap(False, doc, c4d.SNAPMODE_EDGE)
        snap.EnableSnap(True, doc, c4d.SNAPMODE_POLYGON)
        snap.EnableSnap(False, doc, c4d.SNAPMODE_SPLINE)
        snap.EnableSnap(False, doc, c4d.SNAPMODE_AXIS)
        snap.EnableSnap(False, doc, c4d.SNAPMODE_INTERSECTION)
        snap.EnableSnap(False, doc, c4d.SNAPMODE_MIDPOINT)
        snap.EnableSnap(False, doc, c4d.SNAPMODE_WORKPLANE)
        snap.EnableSnap(False, doc, c4d.SNAPMODE_GUIDE)
    
        drawBD = doc.GetActiveBaseDraw()
        drawBD[c4d.BASEDRAW_DATA_BACKCULL]=True
        drawBD[c4d.BASEDRAW_DATA_DEFORMEDEDIT]=True
        drawBD[c4d.BASEDRAW_DATA_SDISPLAYACTIVE]=0
        doc.SetMode(c4d.Mpoints)
        
        
        doc.AddUndo(c4d.UNDOTYPE_BITS, lowresobj)
        lowresobj.SetBit(c4d.BIT_ACTIVE)
        #c4d.CallCommand(431000059) # Viewport Solo Single
    
        doc.AddUndo(c4d.UNDOTYPE_BITS, highresobj)
        highresobj.DelBit(c4d.BIT_ACTIVE)
        
        
        #Display and Color
        
        displaytag = c4d.BaseTag(c4d.Tdisplay)
        displaytag[c4d.DISPLAYTAG_AFFECT_DISPLAYMODE]=True
        displaytag[c4d.DISPLAYTAG_SDISPLAYMODE]=1
        displaytag[c4d.ID_BASELIST_NAME]="TEMPDISPLAY"
        lowresobj.InsertTag(displaytag)
        doc.AddUndo(c4d.UNDOTYPE_NEW, displaytag)
        
        doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, highresobj)
        highresobj[c4d.ID_BASEOBJECT_XRAY]=True
        highresobj[c4d.ID_BASEOBJECT_USECOLOR]=2
        highresobj[c4d.ID_BASEOBJECT_COLOR]=selobjColor()
            
        
        doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, lowresobj) 
        lowresobj[c4d.ID_BASEOBJECT_USECOLOR]=2
        doc.AddUndo(c4d.UNDOTYPE_CHANGE, lowresobj) 
        lowresobj[c4d.ID_BASEOBJECT_COLOR]=retopomeshColor()

    
    else:  # SLIDE MODE
        
        doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, shrinkwrapdeformer)
        shrinkwrapdeformer.SetName("HB_SlideDeformer")
        doc.AddUndo(c4d.UNDOTYPE_CHANGE_SMALL, highresobj)
        highresobj[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR]=1
        highresobj.SetName("HB_SlideProxy")
        doc.AddUndo(c4d.UNDOTYPE_BITS, highresobj)
        highresobj.DelBit(c4d.BIT_ACTIVE)

    
    doc.EndUndo()
    
    if CTRL==False:
        c4d.CallCommand(450000006) #polypen
    
    c4d.EventAdd()


if __name__=='__main__':
    main()
