1 ///2 moduleisodi.model;
3 4 importcore.time;
5 importstd.string;
6 importstd.random;
7 8 importisodi.pack;
9 importisodi.tests;
10 importisodi.object3d;
11 importisodi.resource;
12 13 14 @safe:
15 16 17 /// Represents a 3D model.18 abstractclassModel : Object3D, WithDrawableResources {
19 20 /// Position in the model is relative to the model's bottom, so if a cell is placed at the same position21 /// as the model, the model will be standing on the cell.22 mixinObject3D.Implement;
23 24 private {
25 26 staticsize_tnextID;
27 size_t_id;
28 29 }
30 31 /// Type of the model.32 conststringtype;
33 34 /// Active animations.35 protectedAnimation[] animations;
36 37 /// Seed to use for RNG calls related to generation of this model's resources.38 ///39 /// It's preferred to sum this with a magic number to ensure unique combinations.40 constulongseed;
41 42 /// Create a new model.43 /// Params:44 /// display = Display to create the model for.45 /// type = Skeleton to use for the model.46 this(Displaydisplay, conststringtype) {
47 48 // TODO: ModelBuilder for Isodi editors.49 50 super(display);
51 this._id = nextID++;
52 this.type = type;
53 this.seed = unpredictableSeed;
54 55 }
56 57 @property58 size_tid() const { return_id; }
59 60 /// Ditto61 staticModelmake(Displaydisplay, conststringtype) {
62 63 returnRenderer.createModel(display, type);
64 65 }
66 67 /// Change the variant used for the node with given ID.68 /// Params:69 /// id = ID of the node to change.70 /// variant = Variant of the bone to be set.71 abstractvoidchangeVariant(stringid, stringvariant);
72 73 /// Randomize the variant used for the node with given ID.74 /// Params:75 /// id = ID of the node to change.76 voidchangeVariant(stringid) {
77 78 assert(0, "unimplemented");
79 80 }
81 82 /// Run an animation.83 /// Params:84 /// type = Type of the animation.85 /// duration = Time it should take for one loop of the animation to complete.86 /// times = How many times the animation should be ran.87 voidanimate(stringtype, Durationduration, uinttimes = 1) {
88 89 // Get the resource90 uintframeCount; // @suppress(dscanner.suspicious.unmodified)91 autoresource = display.packs.getAnimation(type, frameCount);
92 93 // Push the animation94 animations ~= Animation(
95 cast(float) frameCount / duration.total!"msecs" * 1000f,
96 times,
97 resource.match98 );
99 100 }
101 102 /// Run an animation indefinitely.103 /// Params:104 /// type = Type of the animation105 /// duration = Time it should take for one loop of the animation to complete.106 voidanimateInf(stringtype, Durationduration) {
107 108 animate(type, duration, 0);
109 110 }
111 112 // TODO: stopAnimation113 114 ///115 protectedPack.Resource!stringgetBone(constSkeletonNodenode) @trusted {
116 117 // TODO: add support for node.variants118 119 autorng = Mt19937_64(seed + node.parent);
120 121 // Get the texture122 autoglob = display.packs.packGlob(node.name.format!"models/bone/%s/*.png");
123 constfile = glob.matches.choice(rng);
124 125 returnPack.Resource!string(
126 file,
127 glob.pack.getOptions(file)
128 );
129 130 }
131 132 }
133 134 mixinDisplayTest!((display) {
135 136 // Model 1137 display.addCell(Position(), "grass");
138 with (display.addModel(Position(), "wraith-white")) {
139 140 animateInf("breath", 6.seconds);
141 animate("crab", 1.seconds);
142 143 }
144 145 // Model 2146 display.addCell(position(2, 0), "grass");
147 display.addModel(position(2, 0), "wraith-white");
148 149 // Add a cell behind150 display.addCell(position(2, 1, Height(4, 5)), "grass");
151 152 });