C'est bon, j'ai trouvé ma solution.
Je la poste au cas où quelqu'un cherche :
Je créé une classe pour mon élément que je fait dériver de UIElement3D afin d'hériter de toutes les fonctions d'interactions. Dans cette classe, je définit le constructeur de mon modèle 3D de type Model3D puis je définit des propriétés de dépendances dont celles qui m’intéresse particulièrement, un identifiant. Ensuite dans mon code, je peut créer les éléments et les nommer correctement, les placer dans un containerUIElement3D et enregistrer le code XAML du containerUIElement3D. Au chargement du containerUIElement3D.xaml, avec un hittest, je retrouve bien les identifiants :
classe perso dérivée de UIElement3D :
public class Rectangle : UIElement3D
{
// une propriété Modele :
private static readonly DependencyProperty ProprieteModele =
DependencyProperty.Register("Modele",
typeof(Model3D),
typeof(PlanRectangle),
new PropertyMetadata(ProprieteModeleModifiee));
private static void ProprieteModeleModifiee(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PlanRectangle le_plan = (PlanRectangle)d;
le_plan.Visual3DModel = le_plan.Modele;
}
private Model3D Modele
{
get
{
return (Model3D)GetValue(ProprieteModele);
}
set
{
SetValue(ProprieteModele, value);
}
}
// une propriété identifiant (nom) :
public static readonly DependencyProperty PrNom =
DependencyProperty.Register("Nom",
typeof(String),
typeof(PlanRectangle),
new PropertyMetadata("Nom",
PrNomModif));
private static void PrNomModif(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PlanRectangle le_plan = (PlanRectangle)d;
le_plan.InvalidateModel();
}
public string Nom
{
get { return (string)GetValue(PrNom); }
set { SetValue(PrNom, value); }
}
// ... puis d'autres propriétés qui m’intéressent, material, dimension, position etc
// enfin un override de la méthode OnUpdateModel() :
// dans laquelle on définit les points de la 3D, ici un rectangle ( DistanceX, DistanceY et DistanceZ sont définit
// en propriété de dépendance) :
protected override void OnUpdateModel()
{
Model3DGroup plan = new Model3DGroup();
Point3D pt0 = new Point3D(-DistanceX / 2, -DistanceY / 2, DistanceZ / 2);
pt0 += (Vector3D)PositionCentre;
Point3D pt1 = new Point3D(-DistanceX / 2, DistanceY / 2, DistanceZ / 2);
pt1 += (Vector3D)PositionCentre;
Point3D pt2 = new Point3D(DistanceX / 2, DistanceY / 2, DistanceZ / 2);
pt2 += (Vector3D)PositionCentre;
Point3D pt3 = new Point3D(DistanceX / 2, -DistanceY / 2, DistanceZ / 2);
pt3 += (Vector3D)PositionCentre;
GeometryModel3D rectangle = ModeliserFaceRectangle(pt3, pt2, pt1, pt0);
plan.Children.Add(rectangle);
Modele = plan;
}
//modeliser une face rectangulaire avec textureFace définit en propriété de dépendance
private GeometryModel3D ModeliserFaceRectangle(Point3D pt1, Point3D pt2, Point3D pt3, Point3D pt4)
{
GeometryModel3D face = new GeometryModel3D();
MeshGeometry3D maillage = new MeshGeometry3D();
maillage.Positions.Add(pt1);
maillage.Positions.Add(pt2);
maillage.Positions.Add(pt3);
maillage.Positions.Add(pt4);
maillage.TriangleIndices.Add(0);
maillage.TriangleIndices.Add(1);
maillage.TriangleIndices.Add(2);
maillage.TriangleIndices.Add(2);
maillage.TriangleIndices.Add(3);
maillage.TriangleIndices.Add(0);
maillage.TextureCoordinates.Add(new Point(1, 1));
maillage.TextureCoordinates.Add(new Point(1, 0));
maillage.TextureCoordinates.Add(new Point(0, 0));
maillage.TextureCoordinates.Add(new Point(0, 1));
face.Geometry = maillage;
face.Material = this.TextureFace;
face.BackMaterial = this.TextureFace;
return face;
}
//---------- Puis dans mon appli, je peut appeler la construction de mon rectangle, lui donner un nom et le placer
//dans un container et enregistrer le tout.
// définition du rectangle
NomDuNamespace.Rectangle unPlan = new NomDuNamespace.Rectangle ();
unPlan.PositionCentre = new Point3D(-1, 0, 0);
unPlan.DistanceX = 1;
unPlan.DistanceY = 1;
unPlan.DistanceZ = 1;
unPlan.TextureFace = new DiffuseMaterial(new SolidColorBrush(Colors.Red));
// Attribution d'un nom
unPlan.Nom = "UnPlanUIELEMENT3D";
//Ajout dans le containerTest :
ContainerUIElement3D containerTest = new ContainerUIElement3D();
containerTest.Children.Add(unPlan);
// Ajout du container au viewport (avec viewport = nom du viewport3D):
viewport.Children.Add(containerTest);
// Pour enregistrer le contenu 3D du containerTest, on copie le xaml dans le presse papier :
Clipboard.SetText(System.Windows.Markup.XamlWriter.Save(containerTest));
// Ensuite, je choisit une SafeFileDialog pour enregistrer le fichier en récupérant le contenu du presse papier
Microsoft.Win32.SaveFileDialog fenSFD = new Microsoft.Win32.SaveFileDialog();
fenSFD.DefaultExt = ".xaml";
fenSFD.Filter = "Document 3D (.xaml)|*.xaml";
Nullable result = fenSFD.ShowDialog();
if (result == true)
{
string fichier = fenSFD.FileName.ToString();
StreamWriter fluxEcriture = new StreamWriter(fichier);
XamlWriter.Save(Clipboard.GetText(), fluxEcriture);
fluxEcriture.Close();
}
// Au chargement, il suffit de parser le contenu du fichier (ajouter des try/catch) :
Microsoft.Win32.OpenFileDialog fenOFD = new Microsoft.Win32.OpenFileDialog();
fenOFD.DefaultExt = ".xaml";
fenOFD.Filter = "Document 3D (.xaml)|*.xaml";
Nullable result = fenOFD.ShowDialog();
if (result == true)
{
var fichierXaml = File.Open(fenOFD.FileName.ToString(), FileMode.Open);
string xaml = XamlReader.Load(fichierXaml).ToString();
// ajouter un try pour gérer l'exception au cas où le fichier chargé ne serait pas du type //ContainerUIElement3D
// On parse le contenu du fichier dans un ContainerUIElement3D
ContainerUIElement3D containerCharge = XamlReader.Parse(xaml) as ContainerUIElement3D;
// On fait une boucle pour extraire tout les UIElement3D contenu dans le containerUIElement3D
foreach (UIElement3D element in containerCharge.Children)
{
// Si l'élément en cours est de type Rectangle alors :
if (element is NomDuNamespace.rectangle )
{
// On récupère la 3D de l'élément pour en extraire son identifiant(nom)
NomDuNamespace.rectangle rect = (NomDuNamespace.rectangle )element;
// Une string pour récupèrer le nom
string namRectangle = rect.Nom;
// une éventuelle messagebox pour vérifier qu'on lit bien un nom d'élément
//MessageBox.Show(namRectangle);
// Puis on ajoute le contenu dans le viewport
viewport.Children.Add(containerCharge);
}
}
}
// Ensuite, on peut faire un HitTest sur le viewport pour identifier si on est sur un élément 3D :
// a mettre dans une méthode avec des try/catch
// Renvoie la position du pointeur dans le viewport3D
Point positionSouris = Mouse.GetPosition(viewport);
HitTestResult result = VisualTreeHelper.HitTest(viewport, positionSouris);
if (result.VisualHit is NomDuNamespace.Rectangle )
{
NomDuNamespace.Rectangle contClick = (NomDuNamespace.Rectangle )result.VisualHit;
// vérification que l'on récupère bien le nom de l'objet cliqué :
string test = contClick.Nom;
MessageBox.Show(test);
}
Voilà, je ne sais pas si j'ai étais très claire ni c'est une bonne façon de faire m'enfin ça fait la job pour moi.