Mayaのシーンは、DGとDAGによって作られています。
そのうちの、DGと同じ原理の仕組みを作ってみましょう。
DGでは、あるノードのアトリビュート値が変更されると、
そのアトリビュートにコネクトされたアトリビュートの値が、
自動的に同じ値に変更される必要があります。
この仕組みを作るためには、デザインパターンの一種であるオブザーバ(Observer)パターンを応用します。
あるオブジェクトの状態が変化したときに、他のオブジェクトにそれを知らせる仕組みです。
オブザーバ・パターンでは、そのために状態が変化したことを知らせる専用のメソッドを定義します。
以下の例では、nmaya1.pyのnotify_observersメソッドなどが、
コネクトしたノードに状態が変化したことを知らせるメソッドになっています。
以下のスクリプトは、オブザーバ・パターンの原理を利用して、
MayaのDGと似た動作をシミュレートしたものです。
なるべく、スクリプトをシンプルにするために、ノードのアトリビュートは一つだけとします。
また、アトリビュートにコネクトできるアトリビュートも一つだけです。
このスクリプトを実行するとプロンプトが表示されるので、
ノードを作成し、メソッドを実行してゆきます。
作成できるノードの種類は以下の1種類だけです。
ノードで使用できるメソッドの種類は以下の通りです。
その他、以下のメソッドを使用します。
import sys
class Node(object):
def __init__(self, attr=0):
self.attr = attr
self.observer = None
def update(self, node):
self.setAttr(node.getAttr())
def notify_observers(self):
if self.observer:
self.observer.update(self)
def connectAttr(self, node):
self.observer = node
self.notify_observers()
def getAttr(self):
return self.attr
def setAttr(self, val):
self.attr = val
self.notify_observers()
while(1):
s = raw_input('command: ')
exec(s)
import sys
class Node(object):
def __init__(self, attr=0):
self.attr = attr
self.observer = None
def update(self, node):
self.setAttr(node.getAttr())
def notify_observers(self):
if self.observer:
self.observer.update(self)
def connectAttr(self, node):
self.observer = node
self.notify_observers()
def getAttr(self):
return self.attr
def setAttr(self, val):
self.attr = val
self.notify_observers()
while(1):
s = raw_input('command: ')
exec(s)
上のnmaya1.pyを応用して、シンプルなモデリングができるシステムに改良してみます。
作成できるノードの種類は以下の通りです。
これらのノードは、Nodebaseクラスを継承して作成してあります。
ノード、その他で使用できるメソッドの種類は以下の通りです。
import sys
class ObjectSet(object):
def __init__(self, sobjs=None):
self.objs = []
self.saveobjs = sobjs
def setObj(self, obj):
self.objs.append(obj)
def save(self):
if self.saveobjs == None:
return
self.saveobjs(self.objs)
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
class TriPoly(object):
def __init__(self, p1, p2, p3):
self.p = [[p1, p2, p3]]
class Nodebase(object):
def __init__(self, attr=None):
self.attr = attr
self.observer = None
def update(self, node):
self.setAttr(node.getAttr())
def notify_observers(self):
if self.observer:
self.observer.update(self)
def connectAttr(self, node):
self.observer = node
self.notify_observers()
def getAttr(self):
return self.attr
def setAttr(self, val):
self.attr = val
self.notify_observers()
class MakeTriPoly(Nodebase):
def __init__(self):
tp = TriPoly(Point(0, 0), Point(0, 1), Point(1, 0))
Nodebase.__init__(self, tp)
class MoveTriPoly(Nodebase):
def __init__(self, n, x, y):
self.num = n
self.x = x
self.y = y
Nodebase.__init__(self)
def update(self, node):
tp = node.getAttr()
f = tp.p[0]
f[self.num].x += self.x
f[self.num].y += self.y
self.setAttr(tp)
class Shape(Nodebase):
objset = None
def __init__(self):
if self.objset:
self.objset.setObj(self)
Nodebase.__init__(self)
def saveAllObjs(objs):
n = 0
for o in objs:
print 'g tri' + str(n)
fs = o.getAttr()
for p in fs.p:
print 'vn 0 1 0'
print 'v ',p[0].x, ' 0 ',p[0].y
print 'v ',p[1].x, ' 0 ',p[1].y
print 'v ',p[2].x, ' 0 ',p[2].y
print 'f ' + str(3*n+1) + '//' + str(n+1) + ' ' + str(3*n+2) + '//' + str(n+1) + ' ' + str(3*n+3) + '//' + str(n+1)
n += 1
Shape.objset = ObjectSet(saveAllObjs)
execfile(sys.argv[1])
m1 = MakeTriPoly()
s1 = Shape()
m1.connectAttr(s1)
Shape.objset.save()
sys.exit(0)
m1 = MakeTriPoly()
s1 = Shape()
m1.connectAttr(s1)
m2 = MakeTriPoly()
mv1 = MoveTriPoly(0, 0, 1)
m2.connectAttr(mv1)
mv2 = MoveTriPoly(1, 1, 0)
mv1.connectAttr(mv2)
s2 = Shape()
mv2.connectAttr(s2)
Shape.objset.save()
sys.exit(0)
for x in range(0, 10):
for y in range(0, 10):
m1 = MakeTriPoly()
mv1 = MoveTriPoly(0, x, y)
m1.connectAttr(mv1)
mv2 = MoveTriPoly(1, x, y)
mv1.connectAttr(mv2)
mv3 = MoveTriPoly(2, x, y)
mv2.connectAttr(mv3)
s1 = Shape()
mv3.connectAttr(s1)
m2 = MakeTriPoly()
mv4 = MoveTriPoly(0, x, y + 1)
m2.connectAttr(mv4)
mv5 = MoveTriPoly(1, x + 1, y)
mv4.connectAttr(mv5)
mv6 = MoveTriPoly(2, x, y)
mv5.connectAttr(mv6)
s2 = Shape()
mv6.connectAttr(s2)
Shape.objset.save()
sys.exit(0)
上のnmaya2.pyから、アトリビュートをいくつでも追加できるように改良してみます。
作成できるノードの種類と、アトリビュートは以下の通りです。
import sys
class ObjectSet(object):
def __init__(self, sobjs=None):
self.objs = []
self.saveobjs = sobjs
def setObj(self, obj):
self.objs.append(obj)
def save(self):
if self.saveobjs == None:
return
self.saveobjs(self.objs)
class Point(object):
def __init__(self, x=0, y=0):
self.x = x
self.y = y
class TriPoly(object):
def __init__(self, p1, p2, p3):
self.p = [[p1, p2, p3]]
class Nodebase(object):
def __init__(self, attr={}):
self.attr = attr
self.observer = {}
def update(self, node, src, dest):
self.setAttr(dest, node.getAttr(src))
self.compute()
def notify_observers(self, src):
if self.observer.has_key(src):
obs = self.observer[src]
obs[0].update(self, src, obs[1])
def compute(self):
pass
def connectAttr(self, src, node, dest):
self.observer[src] = [node, dest]
self.notify_observers(src)
def getAttr(self, aname):
return self.attr[aname]
def setAttr(self, aname, val):
self.attr[aname] = val
self.notify_observers(aname)
def addAttr(self, aname, default=0.0):
self.attr[aname] = default
class MakeTriPoly(Nodebase):
def __init__(self):
tp = TriPoly(Point(0, 0), Point(0, 1), Point(1, 0))
attr = {'out': tp}
Nodebase.__init__(self, attr)
class MoveTriPoly(Nodebase):
def __init__(self, n=0, x=0.0, y=0.0):
attr = {'in': None, 'out': None, 'num': n, 'mx': x, 'my': y}
Nodebase.__init__(self, attr)
def compute(self):
tp = self.getAttr('in')
f = tp.p[0]
num = self.getAttr('num')
f[num].x += self.getAttr('mx')
f[num].y += self.getAttr('my')
self.setAttr('out', tp)
self.notify_observers('out')
class Shape(Nodebase):
objset = None
def __init__(self):
if self.objset:
self.objset.setObj(self)
attr = {'in': None, 'out': None}
Nodebase.__init__(self, attr)
def compute(self):
tp = self.getAttr('in')
self.setAttr('out', tp)
self.notify_observers('out')
def saveAllObjs(objs):
n = 0
for o in objs:
print 'g tri' + str(n)
fs = o.getAttr('out')
for p in fs.p:
print 'vn 0 1 0'
print 'v ',p[0].x, ' 0 ',p[0].y
print 'v ',p[1].x, ' 0 ',p[1].y
print 'v ',p[2].x, ' 0 ',p[2].y
print 'f ' + str(3*n+1) + '//' + str(n+1) + ' ' + str(3*n+2) + '//' + str(n+1) + ' ' + str(3*n+3) + '//' + str(n+1)
n += 1
Shape.objset = ObjectSet(saveAllObjs)
execfile(sys.argv[1])
for x in range(0, 10):
for y in range(0, 10):
m1 = MakeTriPoly()
mv1 = MoveTriPoly()
mv1.setAttr('num', 0)
mv1.setAttr('mx', x)
mv1.setAttr('my', y)
m1.connectAttr('out', mv1, 'in')
mv2 = MoveTriPoly()
mv2.setAttr('num', 1)
mv2.setAttr('mx', x)
mv2.setAttr('my', y)
mv1.connectAttr('out', mv2, 'in')
mv3 = MoveTriPoly()
mv3.setAttr('num', 2)
mv3.setAttr('mx', x)
mv3.setAttr('my', y)
mv2.connectAttr('out', mv3, 'in')
s1 = Shape()
mv3.connectAttr('out', s1, 'in')
m2 = MakeTriPoly()
mv4 = MoveTriPoly()
mv4.setAttr('num', 0)
mv4.setAttr('mx', x)
mv4.setAttr('my', y + 1)
m2.connectAttr('out', mv4, 'in')
mv5 = MoveTriPoly()
mv5.setAttr('num', 1)
mv5.setAttr('mx', x + 1)
mv5.setAttr('my', y)
mv4.connectAttr('out', mv5, 'in')
mv6 = MoveTriPoly()
mv6.setAttr('num', 2)
mv6.setAttr('mx', x)
mv6.setAttr('my', y)
mv5.connectAttr('out', mv6, 'in')
s2 = Shape()
mv6.connectAttr('out', s2, 'in')
Shape.objset.save()
sys.exit(0)
これらのサンプルスクリプトでは、以下のような問題点・改良点があります。
m1 = MakeTriPoly()
mv1 = MoveTriPoly(0, 1, 0)
mv1.setAttr('mx', 3)
m1.connectAttr('out', mv1, 'in')
s1 = Shape()
mv1.connectAttr('out', s1, 'in')
poly = mv1.getAttr('out')
print poly.p[0][0].x
mv1.setAttr('mx', 5)
poly = mv1.getAttr('out')
print poly.p[0][0].x