This article explains how KRI engine features can be added from the outside. You can find all the extension mechanisms of the engine described here. We will also go step-by-step though a complete example of mesh morphing (shape keys in Blender) being implemented as a separate module.
You would have to modify some exporter code in order to add functionality to this stage. However, the native scene format has a chunk structure (Exporter), so adding new chunks will not break existing data loading procedure (just make sure you have no name collosions in chunk names). Here is an example of a chunk export:
out.begin('t_seq') # image sequence out.pack( '3H', user.frames, user.offset, user.start_frame ) # data out.end() # finish chunk
Loaders are registered in data managers and keyed by the return type. Here is an example loader of the text files into strings:
public class Text( kri.data.ILoaderGen[of string] ): public def read(path as string) as string: return System.IO.File.ReadAllText(path) ... loadText = Text() kri.Ant.Inst.dataMan.register(loadText)
kri.data.Switch chooses an approriate loader based on the filename extension. Here is an application example (from Native.boo):
swImage = kri.data.Switch[of kri.buf.Texture]() swImage.ext['.tga'] = image.Targa() resMan.register( swImage ) # local data manager
KRI engine has an extension interface. Each extension adds some chunk readers to the native loader upon its construction. These are core extensions added automatically: ExMaterial, ExMesh, ExAnim and ExObject. They can be accessed via engine core: kri.Ant.Inst.loaders.*
Entity's store vertex buffer storage, as well as a corresponding mesh, may contain attributes you put in them. They will be accessed only by the shaders with matching input names and types.
You can add custom tags to entities and use them by your own renderers (see Entity).
Material in KRI is a container for meta data (see MetaData). You can have entirely new materials with your own meta-data and renderer techniques accessing them.
KRI engine allows compound renderers. You can find a chain renderer (is an analog to a list) and a job scheduler among the standard ones. All of them allows adding custom renderers (derived from kri.rend.Basic).
A simple renderer may operate on user-defined vertex attributes.
A render technique may use user-created meta blocks of the material to build its shaders.
The following code has been added to mesh exporting routine:
for sk in (shapes.keys if mesh.shape_keys else ): out.begin('v_shape') # write shape key data out.end()
A tag class has been implemented to hold shape key data:
public class Tag( kri.ITag ): public final name as string public final data = kri.vb.Attrib() public relative as Tag = null [Getter(Dirty)] private dirty as bool = false private val as single = 1f public Value as single: get: return val set: dirty=true; val=value public def constructor(s as string): name = s
An extension class has been implemented. It should be registered by a user who wishes to load morph data.
public class Extra( kri.IExtension ): private def racShape(pl as kri.ani.data.IPlayer, v as single, i as byte): keys = (pl as kri.Entity).enuTags[of Tag]() keys[i-1].Value = v # implementing IExtension def kri.IExtension.attach(nt as kri.load.Native) as void: nt.readers['v_shape'] = pv_shape # register loader anil = kri.Ant.Inst.loaders.animations # register animation channel anil.anid['v.value'] = kri.load.ExAnim.Rac(getReal,racShape) # shape chunk loader public def pv_shape(r as kri.load.Reader) as bool: e = r.geData[of kri.Entity]() # get current entity if not (e and e.mesh): return false tag = Tag( r.getString() ) # create a shape key tag r.getByte() tag.Value = r.getReal() # read tag data ar = kri.load.ExMesh.GetArray[of Vector3]( e.mesh.nVert, r.getVector ) tag.data.init(ar,false) # fill tag data kri.Help.enrich( tag.data, 3, 'vertex' ) e.tags.Add(tag) # add to the entity return true
The Update renderer does all the hard work of interpolating between shape keys in a shader. Refer to the actual source for details (support/morph).
public class Update( kri.rend.Basic ): public def constructor(): pass # make shader public override def process(con as kri.rend.link.Basic) as void: for ent in kri.Scene.Current.entities: keys = ent.enuTags[of Tag]() # 1. check validity of the keys and the entity # 2. assign vertex attributes # 3. draw the mesh with transform feedback
There is a custom animation class designed to shift morphing coefficients. This animation should be played with an active update renderer in order to see the effect on the object.
public class Anim( kri.ani.Loop ): public final k0 as Tag public final k1 as Tag public def constructor(e as kri.Entity, s0 as string, s1 as string): assert s0 and s1 and s0!=s1 k0 = k1 = null for key in e.enuTags[of Tag](): k0 = key if key.name == s0 k1 = key if key.name == s1 assert k0 and k1 protected override def onRate(rate as double) as void: k0.Value = 1.0 - rate k1.Value = rate
This is a part of a typical user code that takes an advantage of the Morph module:
using win = kri.Window('kri.conf',0): # read the scene win.core.extensions.Add(support.morph.Extra()) # register an extension ln = kri.load.Native() at = ln.read('res/test_hair.scene') ent = at.scene.entities # add renderer view = kri.ViewScreen() rchain = kri.rend.Chain() view.ren = rchain win.views.Add( view ) rchain.renders.Add( support.morph.Update() ) # add an animation win.core.anim = al = kri.ani.Scheduler() morphNative = true # programmable action morphExport = false # exporter data curve if morphNative: amorph = support.morph.Anim(ent,'Basis','Key 1') amorph.lTime = 10.0 al.add( amorph ) elif morphExport: al.add( ent.play('Action') ) # main loop win.Run(20.0,20.0)