xoreos  0.0.5
creature.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include "src/common/scopedptr.h"
26 #include "src/common/util.h"
27 #include "src/common/strutil.h"
28 #include "src/common/maths.h"
29 #include "src/common/error.h"
30 
31 #include "src/aurora/language.h"
32 #include "src/aurora/gff3file.h"
33 #include "src/aurora/gff4file.h"
34 #include "src/aurora/gdafile.h"
35 
37 
40 
43 
44 namespace Engines {
45 
46 namespace DragonAge2 {
47 
48 static const uint32 kMORPID = MKTAG('M', 'O', 'R', 'P');
49 static const uint32 kVersion01 = MKTAG('V', '0', '.', '1');
50 
51 static const uint32 kUTCID = MKTAG('U', 'T', 'C', ' ');
52 static const uint32 kUTIID = MKTAG('U', 'T', 'I', ' ');
53 
54 using ::Aurora::GFF3File;
55 using ::Aurora::GFF3Struct;
57 
58 using ::Aurora::GFF4File;
59 using ::Aurora::GFF4Struct;
61 
62 using namespace ::Aurora::GFF4FieldNamesEnum;
63 
65 
67  init();
68 }
69 
70 Creature::Creature(const GFF3Struct &creature) : Object(kObjectTypeCreature) {
71  init();
72 
73  load(creature);
74 }
75 
77  hide();
78 }
79 
81  _isPC = false;
82 
83  _appearanceID = 0xFFFFFFFF;
84 
85  for (size_t i = 0; i < kPartVariationCount; i++)
86  _partVariation[i] = 0xFFFFFFFF;
87 }
88 
89 void Creature::setPosition(float x, float y, float z) {
90  Object::setPosition(x, y, z);
91  Object::getPosition(x, y, z);
92 
93  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
94  (*m)->setPosition(x, y, z);
95 }
96 
97 void Creature::setOrientation(float x, float y, float z, float angle) {
98  Object::setOrientation(x, y, z, angle);
99  Object::getOrientation(x, y, z, angle);
100 
101  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
102  (*m)->setOrientation(x, y, z, angle);
103 }
104 
106  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
107  (*m)->show();
108 }
109 
111  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
112  (*m)->hide();
113 }
114 
115 bool Creature::isPC() const {
116  return _isPC;
117 }
118 
120  _name.setString(LangMan.getCurrentLanguageText(), LangMan.getCurrentGender(), "Fakoo McFakeston");
121  _tag = Common::UString::format("[PC: %s]", _name.getString().c_str());
122 
123  _isPC = true;
124 
125  enableEvents(false);
126 }
127 
129  status("Creature \"%s\" (\"%s\"): \"%s\"",
131 
132  highlight(true);
133 }
134 
136  highlight(false);
137 }
138 
139 void Creature::highlight(bool enabled) {
140  for (Models::iterator m = _models.begin(); m != _models.end(); ++m)
141  (*m)->drawBound(enabled);
142 }
143 
144 bool Creature::click(Object *triggerer) {
145  runScript(kEventTypeClick , this, triggerer);
146  runScript(kEventTypeConversation, this, triggerer);
147 
148  return true;
149 }
150 
151 void Creature::load(const GFF3Struct &creature) {
152  _resRef = creature.getString("TemplateResRef");
153 
155  if (!_resRef.empty())
157 
158  load(creature, utc ? &utc->getTopLevel() : 0);
159 }
160 
162  if (row == Aurora::GDAFile::kInvalidRow)
163  return "";
164 
165  const Common::UString race = gda.getString(row, "ModelRace");
166  const Common::UString gender = gda.getString(row, "ModelGenderOverride");
167 
168  Common::UString prefix = race + gender;
169  if (prefix.empty())
170  prefix = "shared";
171 
172  return prefix;
173 }
174 
176  const Common::UString &prefix) {
177  if (row == Aurora::GDAFile::kInvalidRow)
178  return "";
179 
180  const Common::UString modelPath = gda.getString(row, "ModelPath");
181  const Common::UString model = gda.getString(row, "Model");
182 
183  if (modelPath.empty() && model.empty())
184  return "";
185 
186  return modelPath + "\\" + prefix + "\\" + model;
187 }
188 
190  uint8 *armorType) const {
191 
192  if (armorType)
193  *armorType = 0;
194 
195  const Aurora::GDAFile &variations = getMGDA(kWorksheetItemVariations);
196  const size_t variationRow = variations.findRow(variation);
197  if (!variation || (variationRow == Aurora::GDAFile::kInvalidRow))
198  return "";
199 
200  const Common::UString model = createModelPart(variations, variationRow, prefix);
201  if (model.empty())
202  return "";
203 
204  if (armorType)
205  *armorType = variations.getInt(variationRow, "MaterialGroup");
206 
207  return model;
208 }
209 
210 void Creature::loadModelsSimple(const Aurora::GDAFile &gda, size_t row) {
211  // Simple, single mesh
212 
213  Model *model = loadModelObject(gda.getString(row, "ModelName"));
214  if (model)
215  _models.push_back(model);
216 }
217 
218 void Creature::loadModelsWelded(const Aurora::GDAFile &gda, size_t row) {
219  // Welded, single mesh with baked weapons
220 
221  Model *model = loadModelObject(gda.getString(row, "ModelName"));
222  if (model)
223  _models.push_back(model);
224 }
225 
226 void Creature::loadModelsHead(const Aurora::GDAFile &gda, size_t row) {
227  // Head, single mesh for body + head model
228 
229  Model *model = loadModelObject(gda.getString(row, "ModelName"));
230  if (model)
231  _models.push_back(model);
232 
233  const Common::UString prefix = createModelPrefix(gda, row);
234 
235  bool haveHelm = false;
236  if ((model = loadModelObject(findEquipModel(kInventorySlotHead, prefix)))) {
237  haveHelm = true;
238 
239  _models.push_back(model);
240  }
241 
242  if (!_headMorph.empty())
243  loadModelsHeadMorph(!haveHelm);
244  else
245  loadModelsHeadList(gda, row, !haveHelm);
246 }
247 
248 void Creature::loadModelsHeadMorph(bool loadHair) {
249  static const size_t kHairPart = 2;
250 
251  try {
252  GFF4File mor(_headMorph, Aurora::kFileTypeMOR, kMORPID);
253  if (mor.getTypeVersion() != kVersion01)
254  throw Common::Exception("Unsupported MOR version %s", Common::debugTag(mor.getTypeVersion()).c_str());
255 
256  std::vector<Common::UString> parts;
257  mor.getTopLevel().getString(kGFF4MorphParts, parts);
258 
259  Model *model = 0;
260  for (size_t i = 0; i < parts.size(); i++) {
261  if ((i == kHairPart) && !loadHair)
262  continue;
263 
264  if ((model = loadModelObject(parts[i])))
265  _models.push_back(model);
266  }
267 
268  } catch (...) {
269  }
270 }
271 
272 void Creature::loadModelsHeadList(const Aurora::GDAFile &gda, size_t row, bool loadHair) {
273  static const size_t kHairPart = 2;
274 
275  static const char * const kSheetColumns[kPartVariationCount] =
276  {"Head_Worksheet", "Eyes_Worksheet", "Hair_Worksheet", "Beard_Worksheet"};
277 
278  const Common::UString prefix = createModelPrefix(gda, row);
279 
280  for (size_t i = 0; i < kPartVariationCount; i++) {
281  const size_t sheetIndex = gda.getInt(row, kSheetColumns[i]);
282  if (sheetIndex == 0)
283  continue;
284 
285  if ((i == kHairPart) && !loadHair)
286  continue;
287 
288  const Aurora::GDAFile &sheet = getMGDA(sheetIndex);
289 
290  const size_t sheetRow = sheet.findRow(_partVariation[i]);
291  if (sheetRow == Aurora::GDAFile::kInvalidRow)
292  continue;
293 
294  Model *model = loadModelObject(createModelPart(sheet, sheetRow, prefix));
295  if (model)
296  _models.push_back(model);
297  }
298 }
299 
300 void Creature::loadModelsParts(const Aurora::GDAFile &gda, size_t row) {
301  // Parts, various models for each body part
302 
303  const Common::UString prefix = createModelPrefix(gda, row);
304 
305  const uint32 chestModel = gda.getInt(row, "ChestModelVariation");
306  const uint32 glovesModel = gda.getInt(row, "GlovesModelVariation");
307  const uint32 bootsModel = gda.getInt(row, "BootsModelVariation");
308  const uint32 helmModel = gda.getInt(row, "HelmModelVariation");
309 
310  static const uint32 kNakedTorso = 10;
311  static const uint32 kNakedGloves = 11;
312  static const uint32 kNakedBoots = 12;
313 
314  uint8 armorType = 0;
315  Model *model = 0;
316 
317  // Torso: creature model -> appearance override -> equipped chest item -> naked
318 
319  model = loadModelObject(gda.getString(row, "ModelName"));
320  if (!model)
321  model = loadModelObject(getItemModel(chestModel, prefix));
322  if (!model)
323  model = loadModelObject(findEquipModel(kInventorySlotChest, prefix, &armorType));
324  if (!model)
325  model = loadModelObject(getItemModel(kNakedTorso, prefix, &armorType));
326 
327  if (model)
328  _models.push_back(model);
329 
330  // Armor of type 5 (clothing) already includes hands and feet in the model...
331  if (armorType != 5) {
332  // Gloves: appearance override -> equipped gloves item -> naked
333 
334  model = loadModelObject(getItemModel(glovesModel, prefix));
335  if (!model)
337  if (!model)
338  model = loadModelObject(getItemModel(kNakedGloves, prefix));
339 
340  if (model)
341  _models.push_back(model);
342 
343  // Boots: appearance override -> equipped boots item -> naked
344 
345  model = loadModelObject(getItemModel(bootsModel, prefix));
346  if (!model)
348  if (!model)
349  model = loadModelObject(getItemModel(kNakedBoots, prefix));
350 
351  if (model)
352  _models.push_back(model);
353  }
354 
355  // Helm: appearance override -> equipped helm item
356 
357  model = loadModelObject(getItemModel(helmModel, prefix));
358  if (!model)
360 
361  bool haveHelm = false;
362  if (model) {
363  haveHelm = true;
364 
365  _models.push_back(model);
366  }
367 
368  // Head: morph -> part list
369 
370  if (!_headMorph.empty())
371  loadModelsHeadMorph(!haveHelm);
372  else
373  loadModelsHeadList(gda, row, !haveHelm);
374 }
375 
377  uint8 *armorType) const {
378  if (armorType)
379  *armorType = 0;
380 
381  const Aurora::GDAFile &baseItems = getMGDA(kWorksheetItems);
382 
383  for (Items::const_iterator item = _items.begin(); item != _items.end(); ++item) {
384  if (item->slot != slot)
385  continue;
386 
387  try {
388  GFF3File uti(item->resRef, Aurora::kFileTypeUTI, kUTIID);
389  const GFF3Struct &utiTop = uti.getTopLevel();
390 
391  const uint32 baseItem = (uint32) ((int32) utiTop.getSint("BaseItem", -1));
392  const uint32 variation = utiTop.getUint("ModelVariation", 0xFFFFFFFF);
393 
394  const size_t itemRow = baseItems.findRow(baseItem);
395  if (itemRow == Aurora::GDAFile::kInvalidRow)
396  continue;
397 
398  return getItemModel(variation, prefix, armorType);
399 
400  } catch (...) {
401  }
402  }
403 
404  return "";
405 }
406 
407 void Creature::load(const GFF3Struct &instance, const GFF3Struct *blueprint = 0) {
408  if (blueprint)
409  loadProperties(*blueprint);
410  loadProperties(instance);
411 
413  const size_t row = gda.findRow(_appearanceID);
414 
415  const Common::UString modelType = gda.getString(row, "ModelType");
416 
417  if (modelType == "S")
418  loadModelsSimple(gda, row);
419  else if (modelType == "W")
420  loadModelsWelded(gda, row);
421  else if (modelType == "H")
422  loadModelsHead(gda, row);
423  else if (modelType == "P")
424  loadModelsParts(gda, row);
425 
426  const float scaleX = gda.getFloat(row, "ModelScaleX", 1.0f);
427  const float scaleY = gda.getFloat(row, "ModelScaleY", 1.0f);
428  const float scaleZ = gda.getFloat(row, "ModelScaleZ", 1.0f);
429 
430  for (Models::iterator m = _models.begin(); m != _models.end(); ++m) {
431  (*m)->setScale(scaleX, scaleY, scaleZ);
432 
433  (*m)->setTag(_tag);
434  (*m)->setClickable(isClickable());
435 
436  _ids.push_back((*m)->getID());
437  }
438 
439  syncPosition();
440  syncOrientation();
441 }
442 
443 void Creature::loadProperties(const GFF3Struct &gff) {
444  // Tag
445  _tag = gff.getString("Tag", _tag);
446 
447  // Name and description
448  gff.getLocString("LocName" , _name);
449  gff.getLocString("Description", _description);
450 
451  // Conversation
452  _conversation = gff.getString("DialogResRef", _conversation);
453 
454  // Static and usable
455  _static = !gff.getBool("Active" , !_static);
456  _usable = gff.getBool("IsSelectable", _usable);
457 
458  // Appearance
459  _appearanceID = gff.getUint("Appearance", _appearanceID);
460 
461  if (gff.hasField("Appearance Data")) {
462  const GFF3Struct &app = gff.getStruct("Appearance Data");
463 
464  _appearanceID = (uint32) ((int32) app.getSint("Appearance_Type", (int32) _appearanceID));
465 
466  _headMorph = app.getString("HeadMorph", _headMorph);
467 
468  if (app.hasField("PartList")) {
469  const GFF3List &parts = app.getList("PartList");
470 
471  for (size_t i = 0; i < kPartVariationCount; i++)
472  _partVariation[i] = (i < parts.size()) ? parts[i]->getUint("PartVariation") : 0xFFFFFFFF;
473  }
474  }
475 
476  // Equipped items
477  if (gff.hasField("Equip_ItemList")) {
478  const GFF3List &itemList = gff.getList("Equip_ItemList");
479  _items.resize(itemList.size());
480 
481  for (size_t i = 0; i < itemList.size(); i++) {
482  _items[i].slot = (InventorySlot) itemList[i]->getID();
483 
484  _items[i].resRef = itemList[i]->getString("TemplateResRef");
485 
486  _items[i].stealable = itemList[i]->getBool("Stealable");
487  _items[i].droopable = itemList[i]->getBool("Droppable");
488 
489  _items[i].setNumber = itemList[i]->getSint("SetNumber", -1);
490  }
491  }
492 
493  // Position
494  if (gff.hasField("XPosition")) {
495  const float position[3] = {
496  (float) gff.getDouble("XPosition"),
497  (float) gff.getDouble("YPosition"),
498  (float) gff.getDouble("ZPosition")
499  };
500 
501  setPosition(position[0], position[1], position[2]);
502  }
503 
504  // Orientation
505  if (gff.hasField("XOrientation")) {
506  const float orientation[4] = {
507  (float) gff.getDouble("XOrientation"),
508  (float) gff.getDouble("YOrientation"),
509  (float) gff.getDouble("ZOrientation"),
510  (float) Common::rad2deg(acos(gff.getDouble("WOrientation")) * 2.0)
511  };
512 
513  setOrientation(orientation[0], orientation[1], orientation[2], orientation[3]);
514  }
515 
516  // Variables and script
517  readVarTable(gff);
518  readScript(gff);
519  enableEvents(true);
520 }
521 
522 } // End of namespace Dragon Age
523 
524 } // End of namespace Engines
Handling version V3.2/V3.3 of BioWare&#39;s GFFs (generic file format).
static const uint32 kVersion01
Definition: campaign.cpp:48
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
void loadModelsParts(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:300
void enter()
The cursor entered the creature.
Definition: creature.cpp:128
Handling BioWare&#39;s GDAs (2DAs, two-dimensional array, within V4.0 GFFs).
int32 getInt(size_t row, uint32 columnHash, int32 def=0) const
Definition: gdafile.cpp:184
void loadModelsSimple(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:210
A creature in a Dragon Age II area.
Head Morph.
Definition: types.h:299
void setString(Language language, LanguageGender gender, const Common::UString &str)
Set the string of that language.
Definition: locstring.cpp:95
bool _usable
Is the object usable?
Definition: object.h:129
A class holding an UTF-8 string.
Definition: ustring.h:48
float getFloat(size_t row, uint32 columnHash, float def=0.0f) const
Definition: gdafile.cpp:202
virtual void setOrientation(float x, float y, float z, float angle)
Set the object&#39;s orientation.
Definition: object.cpp:140
void reset(PointerType o=0)
Resets the pointer with the new value.
Definition: scopedptr.h:87
static const uint32 kUTCID
Definition: creature.cpp:51
virtual void setPosition(float x, float y, float z)
Set the object&#39;s position within its area.
Definition: object.cpp:134
uint8_t uint8
Definition: types.h:200
InventorySlot
Slot in a creature&#39;s inventory.
Definition: types.h:88
Common::UString findEquipModel(InventorySlot slot, const Common::UString &prefix, uint8 *armorType=0) const
Definition: creature.cpp:376
uint32 getID() const
Definition: object.h:45
Mathematical helpers.
void loadModelsHead(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:226
bool click(Object *triggerer=0)
The creature was clicked.
Definition: creature.cpp:144
void setPosition(float x, float y, float z)
Set the creature&#39;s position.
Definition: creature.cpp:89
void enableEvents(bool enabled)
Enable/Disable the handling of all events.
Definition: container.cpp:75
Dragon Age II utility functions.
static const uint32 kMORPID
Definition: creature.cpp:48
bool isClickable() const
Can the player click the object?
Definition: object.cpp:116
bool _isPC
Is the creature a PC?
Definition: creature.h:108
Common::UString _resRef
The object&#39;s resource reference.
Definition: object.h:117
Common::UString _headMorph
Name of the morph file describing the creature&#39;s head.
Definition: creature.h:114
static const size_t kPartVariationCount
Max number of model parts for a creature&#39;s head.
Definition: creature.h:90
size_t findRow(uint32 id) const
Find a row by its ID value.
Definition: gdafile.cpp:95
const Aurora::GDAFile & getMGDA(uint32 id)
Definition: util.cpp:36
Aurora::GFF3File * loadOptionalGFF3(const Common::UString &gff3, Aurora::FileType type, uint32 id, bool repairNWNPremium)
Load a GFF3, but return 0 instead of throwing on error.
Definition: util.cpp:150
bool runScript(EventType event, const Aurora::NWScript::ObjectReference owner=Aurora::NWScript::ObjectReference(), const Aurora::NWScript::ObjectReference triggerer=Aurora::NWScript::ObjectReference())
Definition: container.cpp:100
void highlight(bool enabled)
(Un)Highlight the creature.
Definition: creature.cpp:139
Items _items
All item the creature has currently equipped.
Definition: creature.h:119
Utility templates and functions for working with strings and streams.
const Common::UString & getString(Language language, LanguageGender gender=kLanguageGenderCurrent) const
Get the string of that language.
Definition: locstring.cpp:82
bool _static
Is the object static?
Definition: object.h:128
Exception that provides a stack of explanations.
Definition: error.h:36
A simple scoped smart pointer template.
Common::UString _conversation
The object&#39;s default conversation.
Definition: object.h:124
std::vector< const GFF4Struct * > GFF4List
Definition: types.h:453
Handling version V4.0/V4.1 of BioWare&#39;s GFFs (generic file format).
static Common::UString createModelPart(const Aurora::GDAFile &gda, size_t row, const Common::UString &prefix)
Definition: creature.cpp:175
Basic exceptions to throw.
void show()
Show the creature&#39;s model.
Definition: creature.cpp:105
const char * c_str() const
Return the (utf8 encoded) string data.
Definition: ustring.cpp:249
static UString format(const char *s,...) GCC_PRINTF(1
Print formatted data into an UString object, similar to sprintf().
Definition: ustring.cpp:718
Models _models
The models making up this creature.
Definition: creature.h:122
void loadModelsHeadMorph(bool loadHair=true)
Definition: creature.cpp:248
Utility templates and functions.
void load(const Aurora::GFF3Struct &placeable)
virtual void getOrientation(float &x, float &y, float &z, float &angle) const
Return the object&#39;s orientation.
Definition: object.cpp:126
void loadModelsWelded(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:218
Creature()
Create a dummy creature instance.
Definition: creature.cpp:66
Types and functions related to language.
A 3D model of an object.
bool empty() const
Is the string empty?
Definition: ustring.cpp:245
void leave()
The cursor left the creature.
Definition: creature.cpp:135
static const uint32 kUTIID
Definition: creature.cpp:52
void setOrientation(float x, float y, float z, float angle)
Set the creature&#39;s orientation.
Definition: creature.cpp:97
A scoped plain pointer, allowing pointer-y access and normal deletion.
Definition: scopedptr.h:120
std::vector< const GFF3Struct * > GFF3List
Definition: types.h:449
bool isPC() const
Is the creature a player character?
Definition: creature.cpp:115
static Common::UString createModelPrefix(const Aurora::GDAFile &gda, size_t row)
Definition: creature.cpp:161
Common::UString getItemModel(uint32 variation, const Common::UString &prefix, uint8 *armorType=0) const
Definition: creature.cpp:189
Common::UString getString(size_t row, uint32 columnHash, const Common::UString &def="") const
Definition: gdafile.cpp:165
uint32 _appearanceID
The creature&#39;s appearance; index into the Appearances MGDA.
Definition: creature.h:111
#define LangMan
Shortcut for accessing the language manager.
Definition: language.h:275
void loadProperties(const Aurora::GFF3Struct &gff)
uint32_t uint32
Definition: types.h:204
UString debugTag(uint32 tag, bool trim)
Create an elaborate string from an integer tag, for debugging purposes.
Definition: strutil.cpp:117
Item template (user), GFF.
Definition: types.h:91
void loadModelsHeadList(const Aurora::GDAFile &gda, size_t row, bool loadHair=true)
Definition: creature.cpp:272
Common::UString _tag
Definition: object.h:56
static const size_t kInvalidRow
Definition: gdafile.h:65
uint32 _partVariation[kPartVariationCount]
Indices into the MGDAs describing the creature&#39;s head model parts.
Definition: creature.h:116
virtual void getPosition(float &x, float &y, float &z) const
Return the object&#39;s position within its area.
Definition: object.cpp:120
Aurora::LocString _name
The object&#39;s display name.
Definition: object.h:119
void status(const char *s,...)
Definition: util.cpp:52
static float rad2deg(float rad)
Definition: maths.h:93
Aurora::LocString _description
The object&#39;s description.
Definition: object.h:120
Generic Aurora engines utility functions.
Graphics::Aurora::Model * loadModelObject(const Common::UString &resref, const Common::UString &texture)
Definition: model.cpp:47
void hide()
Hide the creature&#39;s model.
Definition: creature.cpp:110
Creature template (user), GFF.
Definition: types.h:93
void createFakePC()
Create a fake player character creature for testing purposes.
Definition: creature.cpp:119
void readVarTable(const Aurora::GFF3List &varTable)
Class to hold the GFF&#39;d two-dimensional array of a GDA file.
Definition: gdafile.h:62
std::list< uint32 > _ids
The object&#39;s model IDs.
Definition: object.h:131
int32_t int32
Definition: types.h:203
void readScript(const Aurora::GFF3Struct &gff)
Definition: container.cpp:86
Generic Aurora engines model functions.