207 lines
5.1 KiB
Go
207 lines
5.1 KiB
Go
package sceneio
|
|
|
|
import (
|
|
"aego/core/errs"
|
|
"aego/core/ids"
|
|
"aego/core/log"
|
|
"aego/format/aetext"
|
|
"aego/scene"
|
|
"aego/scene/prefab"
|
|
)
|
|
|
|
type MissingPrefab struct {
|
|
Node ids.NodeID
|
|
Prefab ids.AssetID
|
|
Reason string
|
|
}
|
|
|
|
func keySet(nodes map[prefab.Key]*prefab.Node) map[prefab.Key]bool {
|
|
out := make(map[prefab.Key]bool, len(nodes))
|
|
for k := range nodes {
|
|
out[k] = true
|
|
}
|
|
return out
|
|
}
|
|
|
|
func rootName(inst *prefab.Instance, root ids.NodeID) string {
|
|
for k, id := range inst.IDs {
|
|
if id != root {
|
|
continue
|
|
}
|
|
for _, p := range inst.Overrides.Props {
|
|
if p.Node == k && p.Prop.Owner == prefab.OwnerNode && p.Prop.Field == prefab.FieldName {
|
|
return p.Value.S
|
|
}
|
|
}
|
|
}
|
|
return "Missing prefab"
|
|
}
|
|
|
|
func (l *loader) loadInstance(n *aetext.Node, parent scene.NodeHandle) error {
|
|
inst, err := l.readInstance(n)
|
|
if err != nil {
|
|
if l.opt.Strict {
|
|
return err
|
|
}
|
|
l.report.warn(l.name, n.Pos, err.Error())
|
|
return nil
|
|
}
|
|
rootFile := l.localOf(n)
|
|
p, ok := l.lib.Prefab(inst.Prefab)
|
|
if !ok {
|
|
return l.missingInstance(n, parent, rootFile, inst, "prefab "+inst.Prefab.String()+" is not imported")
|
|
}
|
|
base, _, err := prefab.Expand(l.lib, p, nil)
|
|
if err != nil {
|
|
return l.missingInstance(n, parent, rootFile, inst, err.Error())
|
|
}
|
|
tree, orph, err := prefab.Expand(l.lib, p, &inst.Overrides)
|
|
if err != nil {
|
|
return l.missingInstance(n, parent, rootFile, inst, err.Error())
|
|
}
|
|
bn, _ := prefab.Index(base)
|
|
fileOf := map[prefab.Key]ids.NodeID{}
|
|
m := map[ids.NodeID]ids.NodeID{}
|
|
tree.Walk(func(nd, _ *prefab.Node) {
|
|
k := nd.Key
|
|
id, ok := inst.IDs[k]
|
|
switch {
|
|
case nd == tree:
|
|
id = rootFile
|
|
case ok:
|
|
case bn[k] != nil:
|
|
id = ids.NewNodeID()
|
|
default:
|
|
id = k.ID()
|
|
}
|
|
fileOf[k] = id
|
|
m[k.ID()] = id
|
|
})
|
|
tree.MapRefs(m)
|
|
w := &writer{reg: l.reg, lib: l.lib}
|
|
block := w.modelNode(tree, func(nd *prefab.Node) ids.NodeID { return fileOf[nd.Key] }, keySet(bn))
|
|
before := len(l.created)
|
|
if err := l.loadNode(block, parent); err != nil {
|
|
return err
|
|
}
|
|
if len(l.created) == before {
|
|
return nil
|
|
}
|
|
root := l.created[before]
|
|
pi, ok := addInstance(l.scene, root)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
pi.Prefab, pi.Owner, pi.Nested, pi.Missing = inst.Prefab, true, false, false
|
|
pi.Keys = make(map[ids.NodeID]prefab.Key, len(bn))
|
|
for k := range bn {
|
|
fid, ok := fileOf[k]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if h, ok := l.fileIDs[fid]; ok {
|
|
pi.Keys[l.scene.ID(h)] = k
|
|
}
|
|
}
|
|
pi.Orphans = inst.Overrides.Orphans()
|
|
pi.Orphans.Merge(orph)
|
|
pi.Extra, _ = inst.Raw.([]*aetext.Node)
|
|
pi.Block = n.Clone()
|
|
if !orph.Empty() {
|
|
l.report.warn(l.name, n.Pos, "some overrides of "+l.scene.Name(root)+" lost their target in the prefab and are kept as unused")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (l *loader) missingInstance(n *aetext.Node, parent scene.NodeHandle, rootFile ids.NodeID, inst *prefab.Instance, why string) error {
|
|
if l.opt.Strict {
|
|
return errs.New(errs.SceneIOStrict, "prefab instance cannot be loaded", log.F("path", l.name), log.F("reason", why))
|
|
}
|
|
var id ids.NodeID
|
|
if l.opt.KeepIDs {
|
|
id = rootFile
|
|
}
|
|
name := rootName(inst, rootFile)
|
|
h := l.scene.CreateWithID(parent, name, id)
|
|
if h.IsZero() {
|
|
return errs.New(errs.SceneIOParse, "cannot create a node", log.F("path", l.name))
|
|
}
|
|
l.created = append(l.created, h)
|
|
l.fileIDs[rootFile] = h
|
|
if pi, ok := addInstance(l.scene, h); ok {
|
|
pi.Prefab, pi.Missing, pi.Owner, pi.Nested = inst.Prefab, true, false, false
|
|
pi.Block = n.Clone()
|
|
}
|
|
l.report.Missing = append(l.report.Missing, MissingPrefab{Node: l.scene.ID(h), Prefab: inst.Prefab, Reason: why})
|
|
l.report.warn(l.name, n.Pos, "prefab instance "+name+" is kept as is: "+why)
|
|
return nil
|
|
}
|
|
|
|
func (w *writer) instance(s *scene.Scene, h scene.NodeHandle, pi *PrefabInstance) *aetext.Node {
|
|
if !pi.Missing {
|
|
if inst, ok := w.computeInstance(s, h, pi); ok {
|
|
return w.instanceBlock(s.ID(h), inst)
|
|
}
|
|
}
|
|
if pi.Block == nil {
|
|
return nil
|
|
}
|
|
b := pi.Block.Clone()
|
|
b.Label = s.ID(h).String()
|
|
return b
|
|
}
|
|
|
|
func (w *writer) computeInstance(s *scene.Scene, h scene.NodeHandle, pi *PrefabInstance) (*prefab.Instance, bool) {
|
|
if w.lib == nil {
|
|
return nil, false
|
|
}
|
|
p, ok := w.lib.Prefab(pi.Prefab)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
base, _, err := prefab.Expand(w.lib, p, nil)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
flat := (&writer{reg: w.reg, lib: w.lib, flatten: h}).node(s, h)
|
|
if flat == nil {
|
|
return nil, false
|
|
}
|
|
conv := &loader{reg: w.reg, report: &Report{}, name: "instance"}
|
|
tree, err := conv.readModel(flat)
|
|
if err != nil {
|
|
return nil, false
|
|
}
|
|
m := make(map[ids.NodeID]ids.NodeID, len(pi.Keys))
|
|
for id, k := range pi.Keys {
|
|
m[id] = k.ID()
|
|
}
|
|
tree.Walk(func(nd, _ *prefab.Node) {
|
|
if k, ok := pi.Keys[nd.Local]; ok {
|
|
nd.Key = k
|
|
return
|
|
}
|
|
nd.Key = prefab.LocalKey(nd.Local)
|
|
if nd.Instance != nil {
|
|
nd.Origin, nd.Instance = nd.Instance, nil
|
|
}
|
|
})
|
|
if tree.Key != base.Key {
|
|
return nil, false
|
|
}
|
|
tree.MapRefs(m)
|
|
o := prefab.Diff(tree, base)
|
|
o.Merge(pi.Orphans)
|
|
o.Sort()
|
|
inst := &prefab.Instance{Prefab: pi.Prefab, Overrides: o, IDs: map[prefab.Key]ids.NodeID{}}
|
|
tree.Walk(func(nd, _ *prefab.Node) {
|
|
if k, ok := pi.Keys[nd.Local]; ok {
|
|
inst.IDs[k] = nd.Local
|
|
}
|
|
})
|
|
if len(pi.Extra) > 0 {
|
|
inst.Raw = pi.Extra
|
|
}
|
|
return inst, true
|
|
}
|