Hello monde, alors je suis en train de voir dans la doc comment crée un vrai plug-in Coffee, et ici plus exactement un Tag.
Mais bon, c'est pas évident vu que la doc n'est pas franchement des plus easy, surtout quand on ne comprend pas très bien l'english :roll:
Donc, ben j'étais en train de me traduire un peu grossièrement le truc pour y voir plus clair, alors je me dis finalement, tan que j'y suis, autant poster quelques infos sur le sujet même si je ne sais pas si je vais etre compréhensif, n'ayant moi-même, pas encor développer la chose en question :roll:
Je vais prendre pour exemple un petit code facile à comprendre que j'ai entièrement commenté pour l'occasion.
Mon expression suivante permet dans un classique Coffee de fixé automatiquement la profondeur de champ de la camera sur un objet. L'idéal et de l'associer à un Tag Expression Cible, car j'écris pas le codage du target pour ne pas alourdir le code d'exemple, pour rester Easy.
Maintenant le but, c'est de pouvoir transformer ça en plug-in, d'en faire un vrai Tag avec un menu pour choisir la cible.main(doc,op)
{
if(!instanceof(op,CameraObject)) return; // Si l'objet n'est pas une caméra, on quitte
var Obj_Cible = doc->FindObject("Ma_Cible"); if(!Obj_Cible) return;
// Définir l'objet cible (s'il n'existe pas, on quitte l'expression)
var Pos1 = op->GetPosition(); // Position de la caméra
var Pos2 = Obj_Cible->GetPosition(); // Position de la cible
var Distance = vlen(Pos1-Pos2); // Calcul de la distance entre les deux
var Container = op->GetContainer(); // Ouvrir le container de la caméra
Container->SetData(CAMERAOBJECT_DEPTHMIDDLE,Distance); // Inscrire la distance de séparation dans la distance de mire
op->SetContainer(Container); // Renvoyer l'info au container de la camera
}
Préparez le tube d'aspirine, car ça va être tres chaud !!! :coup:
Dans la doc du Coffee (version C4D 6) il y avait un petit pack nommé "Basic Frameworks" dans la partie "exemples", qui contenait 7 structute vide pour chaque type de plugin, où il n'y avait plus qu'à ajouter son code et personnaliser le plugin.
Je n'ai pas retrouvé ce Basic Frameworks dans la doc actuelle (pour C4D 9.1)...
EDIT:
Finallement les 7 fichiers "types" sont dispatchés dans la rubrique "Plugin Types" de la dernière doc du SDK Coffee (ex: menuplugin.cof, filterplugin.cof, animationplugin.cof...)
Oui j'avais trouvé, mais c'est que la structure vide sans exemple des codes, du moins pour les versions Tags. Et les commentaires des fonctions sont un peu légers, surtout quand on ne comprend pas super bien l'anglais :oops:
Je vais tenté de décodé chaque fonction en bon français, et je vous poste tout ça. Comme ça on y verra plus clair, car c'est pas forcement évident de débuter sur l'Expression plugin tags avec tout ça, surtout pour faire ensuite les menus :coup:
Cà c'est une super idée, j'aimerais moi aussi créer un tag (pour mettre en forme mon expression de calcul du temps), et en lisant le SDK, je me suis rendu compte que çà avait l'air assez compliqué. Donc merci de te lancer là-dedans et en plus d'en faire profiter tout le monde :poucehaut:
:bounce: Je me suis pencher sur Coffee et ne reussissant pas a faire les dites fenetre de communication je me suis retrancher sur les DU et l'integration du Coffee dans Xpresso... C'est un peu beaucoup frustrant mais ca marche plus ou moins... :|
Je te suis avec attention
Tout le monde savait que c'était impossible, est venu un con qui ne le savait pas et qui l'a fait!
MarcelPagnol
La difficulté vient surtout du faite qu'on manque d'exemples concrets avec C4D, contrairement à Max ou tu trouves de milliers de scripts sur le web avec les codes ouverts.
La simple doc du SDK est parfois dure à interpréter, ce que j'ai appris, je le dois surtout aux quelques exemples COFFEE qu'on trouve sur le WEB.
Perso, j'étudie un code d'exemple en lisant le doc du SDK pour le décrypter, là ça roule. Mais par contre, commencer un nouveau truc juste avec la doc brute, là c'est vraiment galère !
C'est dommage, car un mellieur développement du coffee peut être un sacré tremplin pour C4D, comme ce fut le cas pour Max avec la multiplication à grande échelle de MaxScript qui bouste à fond le logiciel et fait naître de nouvelles idées.
Bon, la suite demain !
Bon voilà ma 1er description basée sur la structure de base de l'expression Tag.
C'est une structure de base à copier/coller, ensuite il faut remplir les fonctions avec nos codes, notez que toutes fonctions ne sont pas indispensables pour faire notre plug-in selon les cas de figure
Bon si je fais des erreurs ou que vous avez de meilleures précisions à ajouter, n'hésitez pas ! :-)
// Expression Plugin Tag
class MyExpressionPluginTag : ExpressionPluginTag
{
// ici on déclare public toutes les fonctions qui suivent, héritées à la class ExpressionPluginTag
// rien besoin de toucher ici, copier/coller et go !!!
public:
MyExpressionPluginTag();
GetID();
MultipleAllowed();
DisplayAllowed();
GetIcon();
GetHelpText();
UseMenu();
GetName();
Edit();
Copy(dest);
Save(hf);
Load(hf);
Message(type, data);
Execute(doc, op);
Speedup(doc, op);
}
MyExpressionPluginTag::MyExpressionPluginTag()
{
super(); // Appele le constructeur parent
// ici on déclare si-dessous toutes nos variables locales, etc...
}
MyExpressionPluginTag::GetID()
{
// Renvoie l'identification unique de notre plug-in qu'il faut obtenir sur http://www.plugincafe.com
// Faut un ID par plug-in, sinon il peut avoir des conflits !
return 1000001; // Exemple temporaire pour nos tests
}
MyExpressionPluginTag::MultipleAllowed()
{
// Faut renvoyé TRUE si on permet de mettre plusieurs Tag de ce type sur un meme objet
// Mettre FALSE on n'autorise qu'un seul Tag par objet
return FALSE; // exemple
}
MyExpressionPluginTag:isplayAllowed()
{
// Renvoyez TRUE si le Tag montre une icône sur l'objet dans la liste de la scene.. // Mettre FALSE si on veux faire un Tag invisible
return TRUE; // exemple
}
MyExpressionPluginTag::GetIcon()
{
// Ici on va faire un script pour chercher le fichier image qui va nous servir d'icône pour le Tag
}
MyExpressionPluginTag::GetHelpText()
{
// Permets de renvoyer une bulle d'aide avec un texte quand on passe avec la souris sur le Tag
}
MyExpressionPluginTag::UseMenu()
{
// Renvoyez TRUE si le Tag doit être énumérée dans le nouveau Tag menu.
// heu... pas sur, je suppose qu'on puisse ici faire un code pour le placer dans une nouvelle
// liste de Tag quand on clique sur le bouton droit de la souris sur l'objet dans la liste)
// Autrement renvoyez FALSE
return TRUE; // exemple}
MyExpressionPluginTag::GetName()
{
// Pour renvoyer le nom du menu
return "Mon 1er Tag en C.O.F.F.E.E"; // exemple
}
MyExpressionPluginTag::Edit()
{
// Le code à renvoyer quand l'utilisateur double clique sur le Tag (pour créer la boîte de dialogue)
}
MyExpressionPluginTag::Copy(dest)
{
// Si le Tag a des variables locales, les copier au DEST (pas encor capté la ... )
}
MyExpressionPluginTag::Save(hf)
{
// Si le Tag a des variables locales, les sauvé dans l'hyperfile (heu... ? )
}
MyExpressionPluginTag::Load(hf)
{
// Si le Tag a des variables locales, chargez-les dans l'hyperfile ( Ou j'ai mis mon tube d'aspirine ? )
}
MyExpressionPluginTag::Message(type, data)
{
// Accéder aux données de C4D. À voir à quoi cela peut nous servir...}
MyExpressionPluginTag::Execute(doc, op)
{
// Script appelé à chaque redraw, donc exécuté en permanence. Fonctionne un peu comme "main(doc,op) { }", le classique Tag COFFEE qu'on connaît bien
// En fait, c'est là qu'il faut mettre notre code principal
}
MyExpressionPluginTag::Speedup(doc, op)
{
//Faut employer la fonction Touch() pour chaque objet que notre fonction Execute() d'haut dessus modifie, enfin si quelqu'un a une traduction plus claire ? SVP.
//D'après les infos trouver, faut faire un petit script du style
//if(op) Touch(op); return TRUE;
}
main()
{
// Enregistrement de l'expression plug-in tag
Register(MyExpressionPluginTag);
}
Bon, voila deja le code qu'on doit écrire dans la fonction MyExpressionPluginTag::GetIcon() pour afficher l'icône de notre Tag dans la liste des objets.
MyExpressionPluginTag::GetIcon()
{
// Ici on va faire un script pour chercher le fichier image qui va nous servir d'icône pour le Tag
// Création des variables fichier et bitmap
var Bitmap = new(BaseBitmap,1,1);
var File = new(Filename);
// Lecture/Chargement du fichier ico.tif
// dans le répertoire où se trouve notre plug-in
File = GeGetRootFilename();
File->RemoveLast();
File->AddLast("ico.tif");
Bitmap->Load(File);
// Renvoie l'icône à afficher
return Bitmap;
}
Voilà comment il faut mettre notre code principal
En rouge, c'est l'instruction à modifier pour communiquer avec le futur menu à créer
MyExpressionPluginTag::Execute(doc, op)
{
if(!instanceof(op,CameraObject)) return; // Si l'objet n'est pas une caméra, on quitte
var Obj_Cible = doc->FindObject("Ma_Cible"); if(!Obj_Cible) return;
// Définir l'objet cible (s'il n'existe pas, on quitte l'expression)
var Pos1 = op->GetPosition(); // Position de la caméra
var Pos2 = Obj_Cible->GetPosition(); // Position de la cible
var Distance = vlen(Pos1-Pos2); // Calcul de la distance entre les deux
var Container = op->GetContainer(); // Ouvrir le container de la caméra
Container->SetData(CAMERAOBJECT_DEPTHMIDDLE,Distance); // Inscrire la distance de séparation dans la distance de mire
op->SetContainer(Container); // Renvoyer l'info au container de la camera
}
Bon, à ce stade de notre code, si on le met dans un fichier cof du répertoire plug-in de C4D avec une icône, nous pouvons constater qu'il marche déjà très bien.
Cependant comme il n'y a aucune interface utilisateur de fait, on doit renommer manuellement notre cible pour que le script fonctionne, en gros il marche comme le script de base pour le moment
Voilà le fichier d'exemple tout cuit ;-) > http://stephlx.free.fr/c4df/TagTest01.zip
Merci de nous faire partager ca :poucehaut:
J'ai pas encore tout compris mais ca va venir :mrgreen:
Allez la création d'interface maintenant :wink:
La vitesse de la lumière étant supérieure à celle du son, il est donc normal que beaucoup de gens paraissent brillants...
jusqu'à ce qu'ils ouvrent leur gueule.
-------------------------------------------------------------------------------------
Cinema 4D 9.6 - Advanced Render 2.5 - Sketch & Toon - Maxwell Render
Intel Core 2 Quad Q8300 | 8Go DDR2 | Nvidia 430GT
Oui le menu, ça, c'est le plus dur à capter :coup:
Faut définir une Classe dialogue un peu comme la Classe Expression Tag, puis la créer dans notre code
Ensuite faut comprendre comment créé les boutons et tout ça, et pour finir, comme lié les variables du dialogue à notre code
De plus, je me demande si on peu pas non plus créé notre interface aussi sous forme de container, plutot que de type ModalDialog ?? mais bon je vais déjà commencé par la.
Bon, p'tit problème, c'est pas vraiment important pour le moment, mais GetHelpText() ne semble pas renvoyé de message :?
Voila le code pour ouvrir la boite de dialogue par double click sur le Tag, bien sur, pour que ça marche, il faudrait déjà définir la class Dialog dérivé de la classe de base GeModalDialog de C4D qui possède la fonction Open()
Open permet d'ouvrir le menu au coordonnées x, y. Si on met -1 ça créé le menu la ou est la souris
MyExpressionPluginTag::Edit()
{
// Le code ą renvoyer quand l'utilisateur double clique sur le Tag (pour créer la boīte de dialogue)
var Interface = new(Dialog);
Interface->Open(-1,-1);
}
Voilà la création de base de la classe Dialoge, hérité de GeModalDialog
C'est à mettre tout en haut du code ou dans un fichier séparer qu'on appel avec include"Mon_Fichier";
class Dialog : GeModalDialog
{
public: // ici on déclare public toutes les fonctions qui suivent, hériter a là class GeModalDialog
Dialog();
CreateLayout();
}
Dialog:ialog()
{
super(); // Appele le constructeur parent
// ici on déclare si dessous toutes nos variables locales de notre interface
}
Dialog::CreateLayout()
{
// La on va créé notre code d'interface, création des boutons, textes, etc...
}
Je poste juste pour te suivre, c'est plus que passionnant... :efface:
Th. still alive
Voilà un petit code de base pour faire une boîte de dialogue.
Faites une recherche dans le SDK sur GeUserDialog, voir aussi GeUserArea pour voir toutes les fonctions de dialogue et leur forts nombreuses posibilités.
Maintenant quant on double click sur le Tag, ben on ouvre la boîte de dialogue du Tag :-)Dialog::CreateLayout()
{
SetTitle("Ma 1er Interface"); // Titre de la boîte de dialogue
AddStaticText(4000,BFH_FIT,0,0,"Hello World",BORDER_THIN_IN); // Création d'un texte statique avec un encadrement
AddDlgGroup(DR_DLGGROUP_OK|DR_DLGGROUP_CANCEL); // Bouton Ok/Cancel
return TRUE;
}
C'est la méthode de base par codage pur et dur, mais il y a une méthode plus recommander avec le plug-in Resedit qu'on trouve dans le SDK et qui crée notamment les fameux string_us, strings_fr, etc... pour les langues.
A voir comment ça marche...
Voilà, ça, c'est Resedit
Il permet de construire un peu à la manière de visual basic, visuellement une interface, bon c'est bien moins évolué, mais c'est mieux que rien ;-)
Comme on peut le voir, il y a nettement plus de possibilité que les simples DU d'Xpresso.
Quand on a finit, on sauvegarde dans le dossier de notre plug-in et il nous crée auto une série de fichiers est répertoires, ainsi que les variables des différentes langues fr,jp,us,de, etc...
On ne touche pas aux fichiers, il suffit juste de faire include "c4d_symbols.h"; tout au début du script pour pouvoir exploiter l'ensemble dans nos plug-ins.
Maintenant, toute la question est de savoir comment exploiter ça au coeur de notre programme :coup:
On exploite tout cela avec les ID que tu cree dans le Resedit
deja pour charger ton interface
IDD_TEST est cité comme exemple ce qui veut dire que ton fichier resouce est ( IDD_TEST.res )Code:var resource ; // tu accéde au fichier resource dans le main() par exemple avec une variable global resource var file = GeGetRootFilename(); if (!file) return; file->RemoveLast(); resource = new(GeResource,file); // et ici dans CreateLayout() Dialog::CreateLayout() { LoadDialogResource(IDD_TEST, resource, 0) ; return TRUE; }
Excusez-moi, mais vous avez trouvé où la doc sur resEdit, parce que moi je l'avais téléchargé et je n'avais pas trouvé de doc, et en testant vite fait, je n'étais pas arrivé à faire une interface. En fait je ne savais même pas que l'on pouvait faire çà avec.
C'est caché quelque part dans le SDK ou bien c'est sur internet et j'ai mal cherché. Ou encore on doit se fier à son instinct pour l'utiliser...
Ok merci, tout est expliqué dans "Dialog layout" mais faut prendre le temps de décodé, car il n'y a pas d'exemple direct pour la méthode avec Resedit
Pour tél Resedit > http://www.plugincafe.com/sdk/resedit17.zip
Pour comprendre Resedit, faut décodé le SDK, et notamment lire la partie "Dialog layout"
Bon, maintenant faut créer une interface pour notre Tag avec ResEdit
On à juste besoin d'un champ texte pour entrer le nom de la cible et un bouton Ok comme ceci
J'ai nommé l'ID déléments IDC_Target pour le text et ID_Ok pour le bouton.
On sauvegarde sous le nom IDD_TagTest
Ensuite faut bien mettre les fichiers générés par ResEdit dans le dossier de notre plug-in et les disposer exactement comme sur cette capture.
Bon la suite...
On commence par tout en haut
Ensuite tout en bas dans main()// Interface
include "c4d_symbols.h"; // Pour exploiter les symboles de définition de l'interface, les ID des éléments
var plugin_resource; // on déclare globale la variable "plugin_resource"
Pour finir dans CreateLayout()main()
{
// Enregistrement de l'expression plug-in tag
Register(MyExpressionPluginTag);
// pour pouvoir accéder au fichier de ressource de l'interface
var plugin_path = GeGetRootFilename(); if (!plugin_path) return;
plugin_path->RemoveLast();
plugin_resource = new(GeResource,plugin_path);
}
LoadDialogResource( [ Le nom de notre fichier *.res ] , [ Le fichier ressource] ,0);
donc
Esayez, cela marche tres bien, et avec ResEdit c'est pas trop chiant à faire, pour les interface :-)Dialog::CreateLayout(){
return LoadDialogResource(IDD_TagTest,plugin_resource,0); // Chargement des ressources externes
}
Maintenant, il nous reste à encor résoudre une dernière petite chose.
Comment faire communiquer notre boîte de dialogue avec notre code ? :coup:
Donc transmettre les variables de la boîte de dialogue à notre script, et éventuellement le contraie (ça peut aussi nous servir à l'occasion)
Steph cette fois ci, Je remonte le bon sujet :oops:. je le complète et je reviens.
A+
var PLUGIN_ID* *= 1000001 ;
var PLUGIN_NAME = "Mon 1er Tag en C.O.F.F.E.E" ;
var PLUGIN_HELP = "Tag de test" ;
var PLUGIN_ICON = "ico.tif"* * *;
include "c4d_symbols.h"
var resource , Icon ;
//************************************************** **********************
//* * * * * * * * on enum les ids du container du tag
//************************************************** **********************
enum
{
* ID_CIBLE = 100 ,
_dummy
}
//************************************************** **********************
//* * * * * * * * * * *Basic Routines
//************************************************** **********************
LoadIcon(nom)
{
var fichier = GeGetRootFilename() ;
* * fichier->RemoveLast()* *;
* * fichier->AddLast("res") ;
* * fichier->AddLast(nom) ;
* var ic = new(BaseBitmap, 1, 1) ;
* * ic->Load(fichier) ;
* return ic ;
}
//************************************
LoadResource()
{
* var fichier = GeGetRootFilename(); if (!fichier) return NULL ;
* * fichier->RemoveLast() ;
var res = new(GeResource, fichier) ;
* if (!res) return NULL ;
* return res ;
}
//************************************************** **********************
//* * * * *definition de la class du dialogue
//************************************************** **********************
class Dialog : GeModalDialog
{
* private:
* * *var Bc ; // Variable pour le container
* public:
* * *Dialog();
* * *CreateLayout();
* * *GetContainer() ;
* * *SetContainer(bc);
* * *Init() ;
* * *Command(id, msg);
}
Dialog:ialog()
{
super() ;
}
Dialog::CreateLayout()
{
LoadDialogResource (IDD_DIALOG1, resource, 0) ;
}
Dialog::GetContainer()
{
return Bc ; // avec cette fontion on renvoit le container au tag
}
Dialog::SetContainer(bc)
{
Bc = bc ; // avec cette fonction on reçoit le container du tag
}
Dialog::Init()* *
{
* // ici on transmet les valeurs du container Bc au dialogue
SetString(IDC_NAME_TARGET, Bc->GetData(ID_CIBLE)) ;
}
Dialog::Command(id, msg)
{
switch(id)
* *{
* * *// ici on reçoit les valeurs du dialogue et on les Stockes dans le container Bc
* * *case IDC_NAME_TARGET : Bc->SetData(ID_CIBLE, GetString(id)) ; break ;
* *}
Init() ; // avec la fonction Init() on retransmet les valeurs du container Bc au dialogue
}
//************************************************** **********************
//* * * * *definition de la class de l'ExpressionPluginTag
//************************************************** **********************
class MyExpressionPluginTag : ExpressionPluginTag
{
* private:
* * *GetNewContainer() ;
* public:
* * *MyExpressionPluginTag();
* * *GetID();
* * *MultipleAllowed();
* * *DisplayAllowed();
* * *GetIcon();
* * *GetHelpText();
* * *UseMenu();
* * *GetName();
* * *Edit();
* * *Execute(doc, op);
}
MyExpressionPluginTag::MyExpressionPluginTag()
{
* super();
}
MyExpressionPluginTag::GetID()* * * * * * * *
{
return PLUGIN_ID ;
}
MyExpressionPluginTag::MultipleAllowed()* * *
{
* return FALSE ;
}
MyExpressionPluginTag:isplayAllowed()* * *
{
* return TRUE;
}
MyExpressionPluginTag::GetIcon()* * * * * * *
{
* return Icon ;
}
MyExpressionPluginTag::GetHelpText()* * * * *
{
* return PLUGIN_HELP ;
}
MyExpressionPluginTag::UseMenu()
{
* return TRUE ;
}
MyExpressionPluginTag::GetName()
{
return PLUGIN_NAME ;
}
MyExpressionPluginTag::GetNewContainer()
{
* var bc = new(BaseContainer) ;
* * * bc->SetData(ID_CIBLE, "") ;
* return bc ;
}
MyExpressionPluginTag::Edit()
{
* * * * * *// on prend le container du tag
var bc = GetContainer() ;
* * * * * *// on verifi si le container existe, sinon on lu affecte un nouveau container
* * * * * *// avec la fontion privée que j'ai défini par GetNewContainer()
if(!bc->FindIndex(ID_CIBLE)) bc = GetNewContainer() ;
var d = new(Dialog) ;* * *// on creer un dialogue de la class Dialog defini un peut plus haut
* * *d->SetContainer(bc) ; // on transmet le container au dialogue par le fonction SetContainer() de cette class
* * *d->Open(-1,-1) ;* * * // on ouvre le dialogue
* * // si le resultat est validé alors on transmet le container du dialogue au Tag
if (d->GetResult()) SetContainer(d->GetContainer()) ;
}
MyExpressionPluginTag::Execute(doc, op)
{
if(!instanceof(op,CameraObject)) return ;
var bc = GetContainer() ;
var CibleName = bc->GetData(ID_CIBLE) ; if(!CibleName) return ;
var Obj_Cible = doc->FindObject(CibleName); if(!Obj_Cible) return ;
var Pos1 = op->GetPosition() ;
var Pos2 = Obj_Cible->GetPosition() ;
var Distance* = vlen(Pos1-Pos2) ;
var Container = op->GetContainer() ;
Container->SetData(CAMERAOBJECT_DEPTHMIDDLE, Distance) ;
op->SetContainer(Container) ;
}
main()
{
* resource = LoadResource() ; if(!resource) { println(PLUGIN_NAME + " : erreur de resource") ; return ; }
* Icon* * *= LoadIcon(PLUGIN_ICON) ;
* Register(MyExpressionPluginTag);
}
Le fichier http://perso.wanadoo.fr/archizone/fc4d/ExempleTag.zip
ouch ! :o c'est fort ça Majoul :poucehaut: .. ( j'ai rien compris ..mais ça claque :mrgreen: )
c'est facile, je vais t'expliquer.Envoyé par kiteman
:nono:
÷ R19 Studio ÷ cacahuètes ÷
Regardez ICI, moi aussi je fais de la programmation !
De pres on n'y comprand rien mais avec de l'expérience et surtout du recule, tout devient plus clair !!
Ok bon* :mrgreen: j'ai compris je* :arrow:
:odile:
Lima Tango STOP Charlie Oscar Lima STOP Papa Oscar Whisky Echo Lima Lima
Super, merci Majoul, j'étais justement entrain de plancher sur cette problématique pour avancer ma maison paramétrique :coup:
Allez au boulot il faut que je m'y remette :wip:
Pardon de déranger mais pour apprendre je fais un COFFEE de type plugin, j'ai me suis un peu appuyé sur l'exemple de Majoul en changant ce qui ne correspond pas au meme type de plug, mais mais mais j'ai un problème sur le GetId, c'est la même chose, j'ai une variable PLUGIN_ID et à chaque fois sur le Return, il me met Variable Or Function expected. Je pense savoir pourquoi y a ça mais impossible de régler ce problème :cry2:
Dauby pour les intimes !
C'est normal qu'il te sort ce message, le D du GetID() doit être en majuscule, fait gaffe au majuscule et minuscule.