Code Vonc

Subdivision inverse

v 1.1

R13+ OSX Win

TéléchargerDownload
Faire un donMake a donation
Comment installer ?How to install ?
CommentairesComments

Calcule une approximation de l'objet original subdivisé selon la méthode Catmull-Clark.

v 1.1 :
- Correction d'un problème de rafraîchissement.

Copier le dossier « vonc_subdinv » dans le dossier « plugins » du répertoire de Cinéma 4D.

Computes an approximation of the original subdivision object according to Catmull-Clark method.

v 1.1 :
- Fixed a refresh problem.

Copy the folder « vonc_subdinv » to the « plugins » folder of Cinema 4D.






Le module utilise un point de départ pour traiter l'objet. Ce point doit être commun entre le modèle original et subdivisé. Par défaut, ce point est celui d'indice 0, mais il est possible de le changer manuellement, ou de sélectionner le ou les points de départ puis de cocher la case « Sélection » dans les paramètres du module. Cliquez sur le bouton « Act. » pour actualiser la sélection. À utiliser si vous obtenez des résultats bizarres.The plugin need a starting point to process. This should be common between the original model and subdivided. By default, this point is index 0, but it can be changed manually, or selecting the starting points and then check the box « Selection » in the plugin parameters. Click the « Act » button to refresh the selection. Use it if you don't have the expected result.


Codes sourcesSources code

res/c4d_symbols.h

enum
{
    VONC_SUBDINV_NOM	= 1000,
	
    _DUMMY_ELEMENT_
};

res/description/vonc_subdinv.h

#ifndef _vonc_subdinv_H_
#define _vonc_subdinv_H_

enum
{
	VONC_SUBDINV_TYPE			= 1000,
	VONC_SUBDINV_TYPE_CC1		= 0,
	VONC_SUBDINV_TYPE_CC2		= 1,
	VONC_SUBDINV_TYPE_CC3		= 3,
	VONC_SUBDINV_TYPE_CCA		= 2,
	VONC_SUBDINV_SUBD			= 1002,
	VONC_SUBDINV_DEPA			= 1003,
	VONC_SUBDINV_INFO			= 1004,
	VONC_SUBDINV_SELP			= 1005,
	VONC_SUBDINV_INVN			= 1006,
	VONC_SUBDINV_DON			= 1007,
	VONC_SUBDINV_ACTU			= 1008
};

#endif

res/description/vonc_subdinv.res

CONTAINER vonc_subdinv
{
	NAME vonc_subdinv;
	INCLUDE Obase;

	GROUP ID_OBJECTPROPERTIES
	{
		LONG VONC_SUBDINV_TYPE
		{
			CYCLE
			{
				VONC_SUBDINV_TYPE_CC1;
				VONC_SUBDINV_TYPE_CC2;
				VONC_SUBDINV_TYPE_CC3;
				VONC_SUBDINV_TYPE_CCA;
			}
			FIT_H;
		}
		LONG VONC_SUBDINV_SUBD { MIN 0; }
		
		GROUP {
			COLUMNS 3;
			LONG VONC_SUBDINV_DEPA { MIN 0; }
			BOOL VONC_SUBDINV_SELP { }
			BUTTON VONC_SUBDINV_ACTU { }
		}
		BOOL VONC_SUBDINV_INVN { }
		
		SEPARATOR { LINE; }
		
		GROUP {
			STATICTEXT VONC_SUBDINV_INFO { }
			BUTTON VONC_SUBDINV_DON { }
		}
	}
}

res/strings_fr/c4d_strings.str

STRINGTABLE
{
	VONC_SUBDINV_NOM "Subdivision inverse";
}

res/strings_fr/description/vonc_subdinv.str

STRINGTABLE vonc_subdinv
{
	vonc_subdinv "Subdivision inverse";
	
	VONC_SUBDINV_TYPE			"Type";
	VONC_SUBDINV_TYPE_CC1		"Catmull-Clark (estimation 1)";
	VONC_SUBDINV_TYPE_CC2		"Catmull-Clark (estimation 2)";
	VONC_SUBDINV_TYPE_CC3		"Catmull-Clark (estimation 3)";
	VONC_SUBDINV_TYPE_CCA		"Catmull-Clark (adoucie)";
	VONC_SUBDINV_SUBD			"Subdivisions";
	VONC_SUBDINV_DEPA			"Point de d\u00E9part";
	VONC_SUBDINV_INFO			"v 1.1 - C\u00E9sar Vonc - http://code.vonc.fr";
	VONC_SUBDINV_SELP			"S\u00E9lection";
	VONC_SUBDINV_INVN			"Inverser les normales";
	VONC_SUBDINV_DON			"Faire un don";
	VONC_SUBDINV_ACTU			"Act.";
}

vonc_subdinv.pyp

# Subdivision inverse - César Vonc - code.vonc.fr
# v 1.1

import os
import c4d
import webbrowser
from c4d import plugins, utils, bitmaps, Vector
from c4d.utils import Neighbor, VectorAngle

MODULE_ID = 1029594
VONC_SUBDINV_NOM = 1000
VONC_SUBDINV_TYPE = 1000
VONC_SUBDINV_TYPE_CC1 = 0
VONC_SUBDINV_TYPE_CC2 = 1
VONC_SUBDINV_TYPE_CC3 = 3
VONC_SUBDINV_TYPE_CCA = 2
VONC_SUBDINV_SUBD = 1002
VONC_SUBDINV_DEPA = 1003
VONC_SUBDINV_INFO = 1004
VONC_SUBDINV_SELP = 1005
VONC_SUBDINV_INVN = 1006
VONC_SUBDINV_DON = 1007
VONC_SUBDINV_ACTU = 1008

class SubdivisionInverse() :
	obj = None
	doc = None
	obj_nbpts = 0
	obj_nbpol = 0
	obj_n = None
	pts_traites = []
	pol_traites = []
	nouv_polys = []
	pts_orig = []
	nouv_pts = []
	nouv_pts_inv = []
	nouv_coor = []
	pts_corres = []
	pol_corres = []
	cor_poly_polynet = [] # Correspondance [id nouv_polys] = id nouv_polys nettoyé
	cor_polynet_poly = [] # Correspondance [id nouv_polys nettoyé] = id nouv_polys
	_GetAllPolygons = []
	_GetPointPolys = []
	
	def _NettoieTab(self, tab) :
		tab_net = []
		j = 0
		i = 0
		for val in tab :
			if val and len(val) > 2 :
				tab_net.append(val)
				self.cor_poly_polynet.append(j)
				self.cor_polynet_poly.append(i)
				j += 1
			else : self.cor_poly_polynet.append(-1)
			i += 1
		return tab_net
	
	def _SommeVecteur(self, obj, vecs) :
		if not vecs : return Vector()
		s = Vector()
		for v in vecs :
			s += obj.GetPoint(v)
		return s
	
	def _MoyenneVecteur(self, obj, vecs) :
		q = len(vecs)
		if not q : return Vector()
		m = Vector()
		for v in vecs :
			m += obj.GetPoint(v)
		m /= float(q)
		return m
	
	def _RempliCoorArete(self, i, pa, ps, nb) :
		obj = self.obj
		n = self.obj_n
		
		for arete in pa :
			pass
	
	def _RempliCoor(self, i, pa, ps, nb, mode) :
		obj = self.obj
		Po = obj.GetPoint(i)
		
		if mode == 2 : 
			self.nouv_coor[i] = Po
			return
		
		if nb < len(pa) : # Bordure
			n = self.obj_n
			pa_bor = []
			pa_nb = 0
			for arete in pa :
				if pa_nb == 2 : break
				a, b = n.GetEdgePolys(i, arete)
				if a == -1 or b == -1 :
					pa_bor.append(arete)
					pa_nb += 1
			
			Mpa = self._MoyenneVecteur(obj, pa_bor)
			pos = 2.0 * Po - Mpa
		
		elif nb == 3 :
			if mode == 0 :
				Mps = self._MoyenneVecteur(obj, ps)
				pos = Po + (Po - Mps) * 2.0
			
			elif mode == 1 or mode == 3 :
				# Calcul des coordonnées PA fictives
				Mpa = Vector()
				Mps = self._MoyenneVecteur(obj, ps)
				for arete in pa :
					if not self.nouv_coor[arete] :
						paA, psA, nbA = self._RecupPaPs(arete, False)
						if nbA == 3 : continue
						self._RempliCoor(arete, paA, psA, nbA, mode)
					Mpa += self.nouv_coor[arete]
				Mpa /= float(len(pa))
				
				pos = (Mpa-Po) * 2.0 + Po - (Po-Mps) * 0.75 #0.58335
				
				if mode == 3 :
					pos = (pos + Po + (Po - Mps) * 2.0) * 0.5
				#return (i, pa, ps, nb)
		
		else :
			Spa = self._SommeVecteur(obj, pa)
			Sps = self._SommeVecteur(obj, ps)
			pos = (Po*nb)/(nb-3.0) + (-4.0*Spa)/(nb*(nb-3.0)) + Sps/(nb*(nb-3.0))
		
		self.nouv_coor[i] = pos
		
	
	def _RempliPolys(self, i, pts_surf) :
		for p in pts_surf :
			poly = self.nouv_polys[p]
			if not poly : self.nouv_polys[p] = [i]
			else : self.nouv_polys[p].append(i)
			
			# Correspondance polys subdivisés > nouv polys
			pointpolys = self._GetPointPolys[p] #self.obj_n.GetPointPolys(p)
			for pointpoly in pointpolys :
				self.pol_corres[pointpoly] = p
			
	def _RecupPo(self, po, pa) :
		obj = self.obj
		n = self.obj_n
		pts_traites = self.pts_traites
		pol_traites = self.pol_traites
		pos = []
		
		for a in pa :
			polys = self._GetPointPolys[a] #n.GetPointPolys(a)
			points = []
			
			for pol in polys :
				if pol_traites[pol] : continue
				p = self._GetAllPolygons[pol] #obj.GetPolygon(pol)
				points.extend([p.a, p.b, p.c, p.d])
				pol_traites[pol] = True
			points = list(set(points))
			
			for pt in points :
				if pts_traites[pt] : continue
				arete = n.GetEdgePolys(a, pt)
				if arete != (-1, -1) :
					pos.append(pt)
					break
		
		return pos
	
	def _RecupPaPs(self, po, traiteur=True) :
		obj = self.obj
		n = self.obj_n
		pts_traites = self.pts_traites
		pol_traites = self.pol_traites
		
		polys = self._GetPointPolys[po] #n.GetPointPolys(po)
		nb = len(polys)
		points = []
		ps = []
		pa = []
		
		for p in polys :
			if traiteur : pol_traites[p] = True
			p = self._GetAllPolygons[p] #obj.GetPolygon(p)
			points.extend([p.a, p.b, p.c, p.d])
		points = list(set(points))
		
		for pt in points :
			if traiteur :
				self.pts_corres[pt].append(po)
				pts_traites[pt] = True
			if pt == po : continue
			arete = n.GetEdgePolys(po, pt)
			if arete == (-1, -1) :
				ps.append(pt)
			else :
				pa.append(pt)
		
		return pa, ps, nb
	
	def Execute(self, mode, depart, selp, invn) :
		if not self.Initialise(depart) : return
		obj = self.obj
		doc = self.doc
		n = self.obj_n
		pts_traites = self.pts_traites
		pts_orig = self.pts_orig
		nouv_pts = self.nouv_pts
		nouv_pts_inv = self.nouv_pts_inv
		nouv_polys = self.nouv_polys
		nouv_coor = self.nouv_coor
		_RecupPaPs = self._RecupPaPs
		_RecupPo = self._RecupPo
		_RempliPolys = self._RempliPolys
		_RempliCoor = self._RempliCoor
		
		bs = obj.GetPointS()
		pts_a_calc = []
		
		j = 0
		i_n = 0
		i_tot = 1
		for i in pts_orig : # Points de départ pour la recherche des Points Originaux
			i_n += 1
			i_p = 0
			if not pts_traites[i] : # Si le point n'a pas été traité
				pts_traites[i] = True
				# Récupère les points d'arête, de surface et le nombre de polys autour du point original
				pts_arete, pts_surf, nb_poly_cote = _RecupPaPs(i)
				
				# Calcule les nouvelles coordonnées du PO  # ou renvoie a_calc pour les calculer plus tard
				_RempliCoor(i, pts_arete, pts_surf, nb_poly_cote, mode)
				# a_calc = _RempliCoor(i, pts_arete, pts_surf, nb_poly_cote, mode)
				# if a_calc : pts_a_calc.append(a_calc)
				
				# Ajoute PO aux futurs nouveaux polys
				_RempliPolys(i, pts_surf)
				
				# Récupère les points originaux aux alentours des points d'arête
				pts_orig_tour = _RecupPo(i, pts_arete)
				pts_orig.extend(pts_orig_tour)
				
				nouv_pts_inv[i] = j # Tableau [PO sur l'obj. subdivisé] = PO de l'obj. non subd.
				nouv_pts.append(i) # Liste des PO sur l'obj. subdivisé
				
				i_tot += len(pts_orig_tour)
				i_p = 1
				j += 1
			
			if i_n+i_p >= i_tot : # Si on atteint le bout de pts_orig
				if 0 in pts_traites : # S'il reste des points à traiter
					nouv_depart = pts_traites.index(0)
					pts_orig.append(nouv_depart) # Ajouter le non traité à traiter
					i_tot += 1
		
		
		### -- Création de l'objet -- ###
		
		### -- Récupération des propriétés et sélections -- ###
		proprietes = obj.GetTags()
		_Spolsels = []
		_Npolsels = []
		_Sptnsels = []
		_Nptnsels = []
		_Suvws = []
		_Nuvws = []
		for prop in proprietes :
			if prop.CheckType(c4d.Tpolygonselection) :
				_Npolsels.append(prop)
				_Spolsels.append(prop.GetClone())
			elif prop.CheckType(c4d.Tpointselection) :
				_Nptnsels.append(prop)
				_Sptnsels.append(prop.GetClone())
			elif prop.CheckType(c4d.Tuvw) :
				_Nuvws.append(prop)
				_Suvws.append(prop.GetClone())
		_ASptnsel = obj.GetPointS().GetClone()
		_ANptnsel = obj.GetPointS()
		_ASpolsel = obj.GetPolygonS().GetClone()
		_ANpolsel = obj.GetPolygonS()
		
		### -- Création de l'objet -- ###
		
		nouv_polys_sale = nouv_polys[:]
		nouv_polys = self._NettoieTab(nouv_polys)
		nouv_nbpts = len(nouv_pts)
		nouv_nbpolys = len(nouv_polys)
		
		# objet  = c4d.BaseObject(c4d.Opolygon)
		obj.ResizeObject(nouv_nbpts, nouv_nbpolys)
		
		for pti in xrange(nouv_nbpts) :
			ans_id = nouv_pts[pti]
			nouv_id = nouv_pts_inv[ans_id]
			position = nouv_coor[ans_id]
			obj.SetPoint(pti, position)
		
		posi = obj.GetPoint
		for poli in xrange(nouv_nbpolys) :
			poly = nouv_polys[poli]
			pa = nouv_pts_inv[poly[0]]
			pb = nouv_pts_inv[poly[1]]
			pd = nouv_pts_inv[poly[2]]
			if len(poly) != 3 :
				pc = nouv_pts_inv[poly[3]]
				posa = posi(pa)
				posb = posi(pb)
				posc = posi(pc)
				posd = posi(pd)
				vAB = posa - posb
				vAC = posa - posc
				vAD = posa - posd
				vBA = posb - posa
				vBC = posb - posc
				vCA = posc - posa
				vCD = posc - posd
				# Dénoue les polys si le point D est à l'ouest
				if VectorAngle(vCA, -vAD) < VectorAngle(vCA, -vAB) :
					if VectorAngle(vBA, -vAD) < VectorAngle(vBA, -vAC) :
						pc, pd = pd, pc
				else :
					if VectorAngle(vBC, -vCD) < VectorAngle(vBC, -vCA) :
						pa, pd = pd, pa
			else : pc = pd
			
			cpoly = c4d.CPolygon(pa, pb, pc, pd)
			obj.SetPolygon(poli, cpoly)
		
		obj.Message(c4d.MSG_UPDATE)
		commande = utils.SendModelingCommand(command = c4d.MCOMMAND_ALIGNNORMALS, list = [obj], doc = doc)
		
		if invn : commande = utils.SendModelingCommand(command = c4d.MCOMMAND_REVERSENORMALS, list = [obj], doc = doc)
		
		### -- Modifs des propriétés -- #
		
		cor_polyS_polyN = [] # Correspondance [polys subdivisés] = nouveaux polys NETTOYÉ
		for _p in self.pol_corres : cor_polyS_polyN.append(self.cor_poly_polynet[_p])
		
		for _i, _Npolsel in enumerate(_Npolsels) : # Sélection de polygones
			_Spolsel = _Spolsels[_i]
			_Sbs = _Spolsel.GetBaseSelect()
			_Nbs = _Npolsel.GetBaseSelect()
			_Nbs.DeselectAll()
			for i, sel in enumerate(_Sbs.GetAll(self.obj_nbpol)) :
				if not sel : continue
				_p = cor_polyS_polyN[i]
				if _p != -1 : _Nbs.Select(_p)
		
		for _i, _Nptnsel in enumerate(_Nptnsels) : # Sélection de points
			_Sptnsel = _Sptnsels[_i]
			_Sbs = _Sptnsel.GetBaseSelect()
			_Nbs = _Nptnsel.GetBaseSelect()
			_Nbs.DeselectAll()
			for i, sel in enumerate(_Sbs.GetAll(self.obj_nbpts)) :
				if not sel : continue
				_p = nouv_pts_inv[i]
				if _p != -1 and _p < self.obj_nbpts : _Nbs.Select(_p)
		
		_ANpolsel.DeselectAll() # Sélection de polygones active
		for i, sel in enumerate(_ASpolsel.GetAll(self.obj_nbpol)) :
			if not sel : continue
			_p = cor_polyS_polyN[i]
			if _p != -1 : _ANpolsel.Select(_p)
		
		_ANptnsel.DeselectAll() # Sélection de points active
		for i, sel in enumerate(_ASptnsel.GetAll(self.obj_nbpts)) :
			if not sel : continue
			_p = nouv_pts_inv[i]
			if _p != -1 and _p < self.obj_nbpts : _ANptnsel.Select(_p)
		
		
		for _i, _Nuvw in enumerate(_Nuvws) : # UVW
			_Suvw = _Suvws[_i]
			for i in xrange(_Nuvw.GetDataCount()):
				
				k = self.cor_polynet_poly[i]
				polys = self._GetPointPolys[k]
				# points = nouv_polys[i]
				points = []
				Npoly = obj.GetPolygon(i)
				points.append(nouv_pts[Npoly.a])
				points.append(nouv_pts[Npoly.b])
				points.append(nouv_pts[Npoly.c])
				points.append(nouv_pts[Npoly.d])
				
				abcd = [Vector(), Vector(), Vector(), Vector()]
				for poly in polys :
					pol = self._GetAllPolygons[poly]
					for j, pt in enumerate(points) :
						if pol.a == pt :
							abcd[j] = _Suvw.GetSlow(poly)["a"]
							break
						elif pol.b == pt :
							abcd[j] = _Suvw.GetSlow(poly)["b"]
							break
						elif pol.c == pt :
							abcd[j] = _Suvw.GetSlow(poly)["c"]
							break
						elif pol.d == pt :
							abcd[j] = _Suvw.GetSlow(poly)["d"]
							break
							
				_Nuvw.SetSlow(i, abcd[0], abcd[1], abcd[2], abcd[3])
		
		# c4d.EventAdd()
	
	def Initialise(self, depart) :
		if not self.obj : return
		if not self.doc : return
		obj = self.obj
		if not obj.CheckType(c4d.Opolygon) : return
		self.obj_nbpts = obj.GetPointCount()
		self.obj_nbpol = obj.GetPolygonCount()
		if not self.obj_nbpts or not self.obj_nbpol : return
		self.obj_n = Neighbor()
		self.obj_n.Init(obj)
		self.pts_orig = depart
		self.pts_traites = [False] * self.obj_nbpts
		self.pol_traites = [False] * self.obj_nbpol
		self.nouv_polys = [0] * self.obj_nbpts
		self.nouv_pts = []
		self.nouv_pts_inv = [-1] * self.obj_nbpts
		self.nouv_coor = [0] * self.obj_nbpts
		self.pol_corres = [0] * self.obj_nbpol
		self.pts_corres = [[] for a in range(self.obj_nbpts)]
		self.cor_poly_polynet = []
		self.cor_polynet_poly = []
		self._GetAllPolygons = obj.GetAllPolygons()
		self._GetPointPolys = []
		for i in xrange(self.obj_nbpts) : self._GetPointPolys.append(self.obj_n.GetPointPolys(i))
		return True
	
	def __init__(self, doc, obj) :
		self.obj = obj
		self.doc = doc


class SubdivisionInverseObjet(c4d.plugins.ObjectData):
	subdinv = None
	type = 0
	subd = 1
	depa = 0
	selp = False
	invn = False
	
	def Message(self, node, type, data) :
		if type == c4d.MSG_MENUPREPARE :
			node.SetDeformMode(True)
		
		if type == c4d.MSG_DESCRIPTION_COMMAND :
			id = data['id'][0].id
			if id == VONC_SUBDINV_DON : webbrowser.open("http://code.vonc.fr/", 2, True)
			elif id == VONC_SUBDINV_ACTU : node.SetDirty(c4d.DIRTY_DATA)
		
		return True
	
	
	def Init(self, op) :
		donnees = op.GetDataInstance()
		self.InitAttr(op, int, [VONC_SUBDINV_TYPE])
		self.InitAttr(op, int, [VONC_SUBDINV_SUBD])
		self.InitAttr(op, int, [VONC_SUBDINV_DEPA])
		self.InitAttr(op, bool, [VONC_SUBDINV_SELP])
		self.InitAttr(op, bool, [VONC_SUBDINV_INVN])
		op[VONC_SUBDINV_TYPE] = self.type
		op[VONC_SUBDINV_SUBD] = self.subd
		op[VONC_SUBDINV_DEPA] = self.depa
		op[VONC_SUBDINV_SELP] = self.selp
		op[VONC_SUBDINV_INVN] = self.invn
		return True
	
	def ModifyObject(self, mod, doc, op, op_mg, mod_mg, lod, flags, thread):
		if not op.CheckType(c4d.Opolygon) : return True
		if not op.GetPointCount() : return True
		if not op.GetPolygonCount() : return True
		
		self.type = mod[VONC_SUBDINV_TYPE]
		self.subd = mod[VONC_SUBDINV_SUBD]
		self.depa = mod[VONC_SUBDINV_DEPA]
		self.selp = mod[VONC_SUBDINV_SELP]
		self.invn = mod[VONC_SUBDINV_INVN]
		self.subdinv = SubdivisionInverse(doc, op)
		
		for k in xrange(self.subd) :
			nbp = op.GetPointCount()
			depart = [self.depa % nbp]
			if self.selp :
				depart = []
				bs = op.GetPointS()
				for i, sel in enumerate(bs.GetAll(nbp)) :
					if not sel : continue
					depart.append(i)
			if not depart : depart = [0]
			
			self.subdinv.Execute(self.type, depart, self.selp, self.invn)
		
		return True
		

if __name__ == "__main__":
	 bmp = bitmaps.BaseBitmap()
	 dir, f = os.path.split(__file__)
	 fn = os.path.join(dir, "res", "vonc_subdinv.tif")
	 bmp.InitWith(fn)
	 c4d.plugins.RegisterObjectPlugin(id=MODULE_ID, str=c4d.plugins.GeLoadString(VONC_SUBDINV_NOM),
										g=SubdivisionInverseObjet,
										description="vonc_subdinv",
										icon=bmp,
										info=c4d.OBJECT_MODIFIER)