Zenn
💭

TouchDesigner×microCMS

2024/12/30に公開

はじめに

こちらはTouchDesigner Advent Calendar 2024 19日目の記事です。

私事で恐縮ですが、先月から株式会社ワントゥーテンに入社致しました。
https://www.1-10.com/
前職に比べて現場の仕事が増える為、ずっとやろうとしていた遠隔で非エンジニアの方が体験型コンテンツを更新する仕組みとしてmicroCMSとの連携を実装しました。
microCMSで設定したスキーマをTouchDesignerで読み込み、各パラメーター毎にDATに吐き出す工程をtoxにまとめています。
今回作ったtoxは以下に格納しています。
https://drive.google.com/file/d/1Ch5-9H37Dbze0oVTcHTkqImJ0-omOSC4/view?usp=sharing

microCMS

Web界隈ではよく使われているmicroCMS。
https://microcms.io/
ヘッドレスCMSとはビューがなく、ダッシュボードに登録したデータをAPI経由で投げてくれるだけのシンプルなCMSです。
シンプルゆえに汎用性が高く、今回のようにTouchDesignerと連携することもできます。
APIの作成方法は以下公式ドキュメントやネットに記事が沢山あるのでそちらを参考に作成してみてください。
https://blog.microcms.io/getting-started/

microCMSで扱えるパラメータは以下です。

パラメータ一覧
  1. テキストフィールド
  2. テキストエリア
  3. リッチエディタ
  4. 旧リッチエディタ(非推奨)
  5. 画像
  6. 複数画像
  7. 日時
  8. 真偽値
  9. セレクトフィールド
  10. コンテンツ参照
  11. 複数コンテンツ参照
  12. 数字
  13. カスタム
  14. 繰り返し

かなり多くのを型を扱えますね。
今回TouchDesignerで使いどころが難しいリッチエディタ、旧リッチエディタ(非推奨)、コンテンツ参照、複数コンテンツ参照、カスタム、繰り返しは省きます。
ではこれらのパラメーターをTouchDesignerで取得してみましょう。

TouchDesigner

カスタムパラメーターは以下の通りです。

・Url
microCMSで作成したAPIのURL
・Apikey
microCMSで作成したAPIのキー
・APISchemaJson
microCMSで作成したAPIのスキーマ構造が記述されたJSON

JSONのダウンロード方法

microCMSではAPIのスキーマ構造が記述されたJSONをダウンロードできるようになっています。
画像のAPI設定から「この設定をエクスポートする」を押してダウンロードしましょう。
これを使用することで毎回tox内を書き換えずに環境を作ることができます。

tox内のPython

toxの中身を見てみましょう。

基本重要なのはPythonになります。
中身のCHOPExecuteDATは以下のようになっています。
JSONを元にこれから取得するデータを格納するTableDAT、それらを外に出すOutDATを生成し、取得後順番に振り分けていくような実装になっています。
テーブルは既に生成されているものと比較し足りないものは生成、不要なものは削除するようになっています。

import requests as req
import json

def generateOp(fieldId):
	generate_table_op = parent().create(tableDAT,fieldId)
	generate_table_op.tags.add('generate')
	generate_out_op = parent().create(outDAT,fieldId + "Out")
	generate_out_op.tags.add('generate')
	generate_table_op.outputConnectors[0].connect(generate_out_op.inputConnectors[0])

def clearTableOp(tableOp):
	tableOp.clear(keepSize = False)

def appendTableOp(fieldKind,jsonObj,val):
	table_op = op(jsonObj)
	if(fieldKind == "text")or(fieldKind == "textArea")or(fieldKind == "date")or(fieldKind == "select")or(fieldKind == "boolean")or(fieldKind == "number"):
		table_op.appendRow(val)
	elif(fieldKind == "media"):
		table_op.appendRow([val["url"],val["width"],val["height"]])
	elif(fieldKind == "mediaList"):
		json_images = []
		for json_image in val:
			json_images.append(json_image["url"])
			json_images.append(json_image["width"])
			json_images.append(json_image["height"])
		table_op.appendRow(json_images)

def onOffToOn(channel, sampleIndex, val, prev):
	# APIのスキームが記述されたJSONをパース(microCMSのダッシュボードでダウンロード可能)
	# これから生成したいもの
	file_in = op("FileInSchemaJson").jsonObject
	field_ids = []
	field_kind = []
	for jsonContents in file_in["apiFields"]:
		field_ids.append(jsonContents["fieldId"])
		field_kind.append(jsonContents["kind"])
	# 同一階層内にtableDAT取得
	# すでに生成されてるもの
	table_ops = parent().findChildren(type=tableDAT)
	table_op_name = []
	for table_op in table_ops:
		table_op_name.append(table_op.name)
	
	if len(table_ops)==0: #同一階層内にtableDATが一つもなければJSON通りに生成
		for field_id in field_ids:
			generateOp(field_id)
	elif len(table_ops)<len(field_ids): #すでに生成されてるものとAPIのスキームを照らし合わせ足りないものを判定し生成
		for field_id in field_ids:
			if field_id not in table_op_name:# 同階層のtableDATの中にAPIのスキーマのFieldIDと一致するものがなければ生成
				generateOp(field_id)
	elif len(table_ops)>len(field_ids): #すでに生成されてるものとAPIのスキームを照らし合わせ余分なものを判定し削除
		for table_op in table_ops:
			if table_op.name not in field_ids:
				# 接続しているoutDATが存在すれば削除
				if(op(table_op.outputs)):
					op(table_op.outputs[0]).destroy()
				# 不要なtableDATを削除
				table_op.destroy()	

	# 新しく生成したtableDATも含めて再取得
	table_ops = parent().findChildren(type=tableDAT)
	for table_op in table_ops:
		clearTableOp(table_op)

	# microCMSからjsonを取得
	url = str(parent().par.Url)
	api_key = str(parent().par.Apikey)
	header = {
		'X-MICROCMS-API-KEY': api_key,
		'Content-Type': 'application/json'
	}
	res = req.get(url, headers=header)
	jsonData = res.json()

	# 順番にJSONデータ内の値を各TableDATに入力
	for jsonContents in jsonData["contents"]:
		contentNum = 0
		
		for jsonObj in jsonContents:
			# APIのスキームと関係ないパラメーターは省く
			if jsonObj not in field_ids:
				continue
			appendTableOp(field_kind[contentNum],jsonObj,jsonContents[jsonObj])
			contentNum+=1
	return

def whileOn(channel, sampleIndex, val, prev):
	return

def onOnToOff(channel, sampleIndex, val, prev):
	return

def whileOff(channel, sampleIndex, val, prev):
	return

def onValueChange(channel, sampleIndex, val, prev):
	return

取得したデータの使い道

取得したデータの使い道をそれぞれまとめました。

・テキストフィールド

キャッチコピーなど一行で済む文字に使用。

・テキストエリア

ボディコピーなど複数行ある文字に使用。

・画像

画像のURLとWidth、Heightが取得できるのでWebRenderTOPを使用し画像を取得。
複数画像も同様。

・日時

ClockCHOPと比較し特定の日時に発火するイベントを作成。
ExpressionCHOPに取得した日付の値とClockCHOPの日付の値を入力に入れ、以下の式を入力することで判定が行えます。

1 if me.inputs[0][0] == me.inputs[1][0] else 0

・真偽値

何かしらのフラグに使用。
TouchのCHOPでboolを0,1と認識できない為以下の式で判定。

1 if op("null1")[0,0]=="True" else 0

もしくは数字で代用かScriptCHOPなどPython用に使用してもよいかもしれません。

・セレクトフィールド

要素のだし分けなどに使用。
以下の式で判定。

1 if op("null6")[0,0]=="testA" else 0

・数字

何かしらの閾値やパラメータの調整に使用。

まとめ

遅れてになってしまいましたが今年のアドカレの記事でした。
今年はVJの出演などアウトプットを出す機会もありつつ転職でかなりバタバタした一年でしたが、来年が楽しみなくらい環境が大きく変わりました。
新しい刺激と共にどんどん興味ある事に手を出していきたいと思います。
音をまたやりたいなと思い、早速クリスマスプレゼントとしてYAMAHAのSEQTRAKを購入して遊んでます。
それでは皆様よいお年を!

Discussion

ログインするとコメントできます