Acrobat 3D i zmiana materiałów w modelach, część 2

No Comments

Witam po przerwie. Ostatnio skończyliśmy na przejmowaniu niektórych (wystarczających) właściwości materiału, celem ich skopiowania na obiekt. Dzisiaj dowiemy się, jak te wartości przypisać. Do tego celu stworzyłem pewną klasę, która cały ten proces wykona za nas.

Na początku pragnę jeszcze zwrócić uwagę na coś ciekawego. Mimo, że konkretne meshe mają przypisane konkretne materiały, zmiany dokonujemy w samych materiałach. Acrobat nie zmieni właściwości materiału konkretnego mesha. On po prostu zmieni właściwości materiału przypisanego do tego mesha.

Wyobraźmy sobie taką sytuację. Mamy nasz nieszczęsny długopis. Posiada on korpus i zatyczkę. Mamy materiały na korpus i zatyczkę, załóżmy że “niebieski” i “metal”. Dla zróżnicowania.
I teraz, w naszym przykładzie, odpalamy model z niebieskim korpusem i niebieską zatyczką. Czyli do grupy meshów opisujących korpus przypisujemy materiał niebieski i ten sam do grupy meshów opisujących zatyczkę. Czemu tak podkreślam te grupy? Aby przypomnieć, że meshe są ułożone hierarchicznie – ta wiedza pomoże nam później.

No dobrze, ale produkujemy też długopisy z niebieską zatyczką i metalicznym korpusem. “Nie ma problemu” – powiecie – “przypiszmy materiał metaliczny do korpusu i tyle”.
Też tak myślałem…
Po zmianie właściwości materiału korpusu, zmieni się tez zatyczka. Dlaczego? Bo mesh nie posiada swojej “kopii” danego materiału, tylko referencję do obiektu materiału, który również siedzi w modelu. Zmieniając właściwości materiału jednego mesha, zmieniamy je dla wszystkich meshów, mających przypisany ten sam materiał.

Co zrobić? Trzeba przewidywać. I stworzyć dwa różne materiały, nawet identyczne, dla tych dwóch obiektów.

Kiedy już to wyjaśniliśmy, przejdźmy do wspomnianej przydatnej klasy. Będzie to klasa opisująca obiekt na modelu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
// klasa grupujaca wszystkie podane meshe obiektu,
// celem hurtowego wykonywania na nich operacji
function element(elem)
{
  this.elements = new Array();  // wszystkie meshe tego obiektu
  this.firstElem = elem;  // element nadrzedny calego
 
  // funkcja dodajaca do obiektu wszystkie meshe potomne
  // w stosunku do podanego
  this.children = function(meshName)
  {
    var mesh;
    if(Context().scene.meshes.getByName(meshName))
    {
      mesh = Context().scene.meshes.getByName(meshName);
      if(mesh.material)
        this.elements.push(mesh);
    }
    else
    {
      mesh = Context().scene.nodes.getByName(meshName);
      if(mesh.firstChild)
        this.children(mesh.firstChild.name);  // rekurencja
    }
 
    if(meshName != this.firstElem)
    {
      while(mesh.nextSibling)
      {
        if(Context().scene.meshes.getByName(mesh.nextSibling.name))
        {
          mesh = Context().scene.meshes.getByName(mesh.nextSibling.name);
          if(mesh.material)
            this.elements.push(mesh);
        }
        else
        {
          mesh = Context().scene.nodes.getByName(mesh.nextSibling.name);
          if(mesh.firstChild)
            this.children(mesh.firstChild.name);
        }
      }
    }
  }
 
  // funkcja pozwalajaca na dodanie do elementu
  // kolejnej grupy meshow
  this.addElement = function(elem)
  {
    this.children(elem);
  }
 
  // zmiana wlasciwosci materialu wszystkich zgrupowanych
  // w tym obiekcie meshow
  this.changeMaterial = function(mat)
  {
    this.elements[0].material.ambientColor.set3(mat.ambient[0], mat.ambient[1], mat.ambient[2]);
    this.elements[0].material.diffuseColor.set3(mat.diffuse[0], mat.diffuse[1], mat.diffuse[2]);
    this.elements[0].material.specularColor.set3(mat.specular[0], mat.specular[1], mat.specular[2]);
    this.elements[0].material.emissiveColor.set3(mat.emissive[0], mat.emissive[1], mat.emissive[2]);
 
    this.elements[0].material.specularStrength   = mat.specularStrength;
    this.elements[0].material.reflectionStrength = mat.reflectionStrength;
    this.elements[0].material.opacity = mat.opacity;
    this.elements[0].material.reflectionTexture.setImage(mat.texture);
  }
 
  // przelaczanie widocznosci wszystkich meshow tego obiektu
  this.display = function(onoff)
  {
    for(var ak = 0; ak < this.elements.length; ak++)
      this.elements[ak].visible = onoff;
  }
 
  // po inicjalizacji - pobranie potomkow mesha glownego
  this.children(elem);
}

Za pomocą tych dwóch klas (ExtMaterial() i element()) Możemy dokonać wszelkich manipulacji na poszczególnych meshach, jak i na całych ich grupach.

I teraz kilka słów tytułem objaśnienia. Meshe w modelu grupowane są w postaci drzewka. Klasa element(), otrzymawszy nazwę nadrzędnej jego gałęzi, pobiera wszystkie meshe potomne, rekursywnie.To na wszelki wypadek, by dobrze współpracować z “programistą” modelu 3D. Jednakże klasa element() posiada własny “kontener” meshów i metodę .addElement(), która pozwala nam na grupowanie kilku równorzędnych (chociaż niekoniecznie) gałęzi drzewa meshów. We wspomnianym przykładzie długopisu, zatyczka może być odrębnym elementem, ale jeśli zatyczka składa się z kilku grup meshów (np. “kapturek” i “zasuwka”, cokolwiek by to miało znaczyć), które programista zapomniał złączyć w grupę “zatyczka”, możemy sobie z tym poradzić “ręcznie”, czyli:

  var zatyczka = new element("kapturek");
  zatyczka.addElement("zasuwka");

Myślę, że pozostałych rzeczy nie trzeba tłumaczyć, ale niech będzie. Za pomocą wspomnianego zestawu klas możemy dokonywać podstawowych rzeczy. Po odpowiednim utworzeniu obiektów:

  // utworzenie kopii materialow
  var metaliczny = new ExtMaterial("probki_metal");
  var zielony    = new ExtMaterial("probki_zielony");
  // utworzenie elementow
  var zatyczka = new element("kapturek");
  var korpus   = new element("korpus");
  zatyczka.addElement("zasuwka");  // uzupelnienie elementu "zatyczka"

…możemy rozkoszować się pełną funkcjonalnością, o jaką nam chodzi. Wyjaśniając na przykładach, oprogramujmy cztery przyciski:

1 i 2: pokazywanie/ukrywanie elementu:

  // pokaz:
  zatyczka.display(true);
  // ukryj:
  zatyczka.display(false);

3 i 4: przeskakiwanie pomiędzy materiałami:

  // tu objasnienia chyba nie sa potrzebne?
  // 1.
  zatyczka.changeMaterial(metaliczny);
  // 2.
  korpus.changeMaterial(zielony);

Zachęcam do kombinowania. Jeśli będzie mi się chciało, wyjdzie jeszcze jeden odcinek, w którym złożymy wszystko do kupy i zrobimy ładny interfejs, ale dysponując powyższymi klasami pozostaje już tylko odpowiednie oprogramowanie przycisków/menu etc.

Leave a Reply

Your email address will not be published. Required fields are marked *