#include "FRME.hpp" #include "../DNACommon/TXTR.hpp" namespace DataSpec { namespace DNAMP1 { void FRME::read(athena::io::IStreamReader& __dna_reader) { /* version */ version = __dna_reader.readUint32Big(); /* unk1 */ unk1 = __dna_reader.readUint32Big(); /* modelCount */ modelCount = __dna_reader.readUint32Big(); /* unk3 */ unk3 = __dna_reader.readUint32Big(); /* widgetCount */ widgetCount = __dna_reader.readUint32Big(); /* widgets */ __dna_reader.enumerate(widgets, widgetCount, [this](athena::io::IStreamReader& reader, Widget& w) { w.owner = this; w.read(reader); }); } void FRME::write(athena::io::IStreamWriter& __dna_writer) const { /* version */ __dna_writer.writeUint32Big(version); /* unk1 */ __dna_writer.writeUint32Big(unk1); /* modelCount */ __dna_writer.writeUint32Big(modelCount); /* unk3 */ __dna_writer.writeUint32Big(unk3); /* widgetCount */ __dna_writer.writeUint32Big(widgetCount); /* widgets */ __dna_writer.enumerate(widgets); } size_t FRME::binarySize(size_t __isz) const { __isz = __EnumerateSize(__isz, widgets); return __isz + 20; } void FRME::Widget::read(athena::io::IStreamReader& __dna_reader) { /* type */ type.read(__dna_reader); /* header */ header.read(__dna_reader); switch(type) { case SBIG('BWIG'): widgetInfo.reset(new BWIGInfo); break; case SBIG('HWIG'): widgetInfo.reset(new HWIGInfo); break; case SBIG('CAMR'): widgetInfo.reset(new CAMRInfo); break; case SBIG('LITE'): widgetInfo.reset(new LITEInfo); break; case SBIG('ENRG'): widgetInfo.reset(new ENRGInfo); break; case SBIG('MODL'): widgetInfo.reset(new MODLInfo); break; case SBIG('METR'): widgetInfo.reset(new METRInfo); break; case SBIG('GRUP'): widgetInfo.reset(new GRUPInfo); break; case SBIG('TXPN'): widgetInfo.reset(new TXPNInfo(owner->version)); break; case SBIG('IMGP'): widgetInfo.reset(new IMGPInfo); break; case SBIG('TBGP'): widgetInfo.reset(new TBGPInfo); break; case SBIG('SLGP'): widgetInfo.reset(new SLGPInfo); break; default: Log.report(logvisor::Fatal, _S("Unsupported FRME widget type %.8X"), type.toUint32()); } /* widgetInfo */ widgetInfo->read(__dna_reader); /* isWorker */ isWorker = __dna_reader.readBool(); if (isWorker) { /* workerId */ workerId = __dna_reader.readUint16Big(); } /* origin */ origin = __dna_reader.readVec3fBig(); /* basis[0] */ basis[0] = __dna_reader.readVec3fBig(); /* basis[1] */ basis[1] = __dna_reader.readVec3fBig(); /* basis[2] */ basis[2] = __dna_reader.readVec3fBig(); /* rotationCenter */ rotationCenter = __dna_reader.readVec3fBig(); /* msgCount */ msgCount = __dna_reader.readInt16Big(); /* funcDefCount */ funcDefCount = __dna_reader.readInt16Big(); /* animControllerCount */ animControllerCount = __dna_reader.readInt16Big(); } void FRME::Widget::write(athena::io::IStreamWriter& __dna_writer) const { /* type */ type.write(__dna_writer); /* header */ header.write(__dna_writer); /* widgetInfo */ widgetInfo->write(__dna_writer); /* isWorker */ __dna_writer.writeBool(isWorker); if (isWorker) { /* workerId */ __dna_writer.writeUint16Big(workerId); } /* origin */ __dna_writer.writeVec3fBig(origin); /* basis[0] */ __dna_writer.writeVec3fBig(basis[0]); /* basis[1] */ __dna_writer.writeVec3fBig(basis[1]); /* basis[2] */ __dna_writer.writeVec3fBig(basis[2]); /* rotationCenter */ __dna_writer.writeVec3fBig(rotationCenter); /* msgCount */ __dna_writer.writeInt16Big(msgCount); /* funcDefCount */ __dna_writer.writeInt16Big(funcDefCount); /* animControllerCount */ __dna_writer.writeInt16Big(animControllerCount); } size_t FRME::Widget::binarySize(size_t __isz) const { __isz = type.binarySize(__isz); __isz = header.binarySize(__isz); __isz = widgetInfo->binarySize(__isz); if (isWorker) __isz += 4; return __isz + 67; } void FRME::Widget::CAMRInfo::read(athena::io::IStreamReader& __dna_reader) { projectionType = ProjectionType(__dna_reader.readUint32Big()); if (projectionType == ProjectionType::Perspective) projection.reset(new PerspectiveProjection); else if (projectionType == ProjectionType::Orthographic) projection.reset(new OrthographicProjection); else Log.report(logvisor::Fatal, _S("Invalid CAMR projection mode! %i"), int(projectionType)); projection->read(__dna_reader); } void FRME::Widget::CAMRInfo::write(athena::io::IStreamWriter& __dna_writer) const { if (!projection) Log.report(logvisor::Fatal, _S("Invalid CAMR projection object!")); if (projection->type != projectionType) Log.report(logvisor::Fatal, _S("CAMR projection type does not match actual projection type!")); __dna_writer.writeUint32Big(atUint32(projectionType)); projection->write(__dna_writer); } size_t FRME::Widget::CAMRInfo::binarySize(size_t __isz) const { __isz = projection->binarySize(__isz); return __isz + 4; } void FRME::Widget::LITEInfo::read(athena::io::IStreamReader& __dna_reader) { IWidgetInfo::read(__dna_reader); /* type */ type = ELightType(__dna_reader.readUint32Big()); /* distC */ distC = __dna_reader.readFloatBig(); /* distL */ distL = __dna_reader.readFloatBig(); /* distQ */ distQ = __dna_reader.readFloatBig(); /* angC */ angC = __dna_reader.readFloatBig(); /* angL */ angL = __dna_reader.readFloatBig(); /* angQ */ angQ = __dna_reader.readFloatBig(); /* loadedIdx */ loadedIdx = __dna_reader.readUint32Big(); /* cutoff */ if (type == ELightType::Spot) cutoff = __dna_reader.readFloatBig(); } void FRME::Widget::LITEInfo::write(athena::io::IStreamWriter& __dna_writer) const { IWidgetInfo::write(__dna_writer); /* type */ __dna_writer.writeUint32Big(atUint32(type)); /* distC */ __dna_writer.writeFloatBig(distC); /* distL */ __dna_writer.writeFloatBig(distL); /* distQ */ __dna_writer.writeFloatBig(distQ); /* angC */ __dna_writer.writeFloatBig(angC); /* angL */ __dna_writer.writeFloatBig(angL); /* angQ */ __dna_writer.writeFloatBig(angQ); /* loadedIdx */ __dna_writer.writeUint32Big(loadedIdx); /* cutoff */ if (type == ELightType::Spot) __dna_writer.writeFloatBig(cutoff); } size_t FRME::Widget::LITEInfo::binarySize(size_t __isz) const { __isz = IWidgetInfo::binarySize(__isz); return __isz + ((type == ELightType::Spot) ? 36 : 32); } void FRME::Widget::TXPNInfo::read(athena::io::IStreamReader& __dna_reader) { IWidgetInfo::read(__dna_reader); /* xDim */ xDim = __dna_reader.readFloatBig(); /* zDim */ zDim = __dna_reader.readFloatBig(); /* scaleCenter */ scaleCenter = __dna_reader.readVec3fBig(); /* font */ font.read(__dna_reader); /* unk1 */ wordWrap = __dna_reader.readBool(); /* unk2 */ vertical = __dna_reader.readBool(); /* justification */ justification = Justification(__dna_reader.readUint32Big()); /* verticalJustification */ verticalJustification = VerticalJustification(__dna_reader.readUint32Big()); /* fillColor */ fillColor = __dna_reader.readVec4fBig(); /* outlineColor */ outlineColor = __dna_reader.readVec4fBig(); /* pointScale */ blockExtent = __dna_reader.readVec2fBig(); if (version == 1) { /* jpnFont */ jpnFont.read(__dna_reader); /* jpnPointScale[0] */ jpnPointScale[0] = __dna_reader.readInt32Big(); /* jpnPointScale[0] */ jpnPointScale[1] = __dna_reader.readInt32Big(); } } void FRME::Widget::TXPNInfo::write(athena::io::IStreamWriter& __dna_writer) const { IWidgetInfo::write(__dna_writer); /* xDim */ __dna_writer.writeFloatBig(xDim); /* zDim */ __dna_writer.writeFloatBig(zDim); /* scaleCenter */ __dna_writer.writeVec3fBig(scaleCenter); /* font */ font.write(__dna_writer); /* unk1 */ __dna_writer.writeBool(wordWrap); /* unk2 */ __dna_writer.writeBool(vertical); /* justification */ __dna_writer.writeUint32Big(atUint32(justification)); /* verticalJustification */ __dna_writer.writeUint32Big(atUint32(verticalJustification)); /* fillColor */ __dna_writer.writeVec4fBig(fillColor); /* outlineColor */ __dna_writer.writeVec4fBig(outlineColor); /* pointScale */ __dna_writer.writeVec2fBig(blockExtent); if (version == 1) { /* jpnFont */ jpnFont.write(__dna_writer); /* jpnPointScale[0] */ __dna_writer.writeInt32Big(jpnPointScale[0]); /* jpnPointScale[1] */ __dna_writer.writeInt32Big(jpnPointScale[1]); } } size_t FRME::Widget::TXPNInfo::binarySize(size_t __isz) const { __isz = IWidgetInfo::binarySize(__isz); __isz = font.binarySize(__isz); if (version == 1) __isz = jpnFont.binarySize(__isz); return __isz + (version == 1 ? 78 : 66); } bool FRME::Extract(const SpecBase &dataSpec, PAKEntryReadStream &rs, const hecl::ProjectPath &outPath, PAKRouter &pakRouter, const PAK::Entry &entry, bool force, hecl::BlenderToken& btok, std::function fileChanged) { FRME frme; frme.read(rs); hecl::BlenderConnection& conn = btok.getBlenderConnection(); #if 0 if (!force && outPath.isFile()) return true; #endif if (!conn.createBlend(outPath, hecl::BlenderConnection::BlendType::Frame)) return false; hecl::BlenderConnection::PyOutStream os = conn.beginPythonOut(true); os << "import bpy, math\n" "from mathutils import Matrix, Quaternion\n" "bpy.types.Object.retro_widget_type = bpy.props.StringProperty(name='Retro: FRME Widget Type')\n" "model_draw_flags = [\n" " ('RETRO_SHADELESS', 'Shadeless', '', 0),\n" " ('RETRO_OPAQUE', 'Opaque', '', 1),\n" " ('RETRO_ALPHA', 'Alpha', '', 2),\n" " ('RETRO_ADDITIVE', 'Additive', '', 3),\n" " ('RETRO_ALPHA_ADDITIVE_OVERDRAW', 'Alpha Additive Overdraw', '', 4)]\n" "bpy.types.Object.retro_widget_parent = bpy.props.StringProperty(name='Retro: FRME Widget Parent', description='Refers to internal frame widgets')\n" "bpy.types.Object.retro_widget_use_anim_controller = bpy.props.BoolProperty(name='Retro: Use Animiation Conroller')\n" "bpy.types.Object.retro_widget_default_visible = bpy.props.BoolProperty(name='Retro: Default Visible', description='Sets widget is visible by default')\n" "bpy.types.Object.retro_widget_default_active = bpy.props.BoolProperty(name='Retro: Default Visible', description='Sets widget is cases by default')\n" "bpy.types.Object.retro_widget_cull_faces = bpy.props.BoolProperty(name='Retro: Default Visible', description='Enables face culling')\n" "bpy.types.Object.retro_widget_color = bpy.props.FloatVectorProperty(name='Retro: Color', description='Sets widget color', subtype='COLOR', size=4, min=0.0, max=1.0)\n" "bpy.types.Object.retro_widget_model_draw_flags = bpy.props.EnumProperty(items=model_draw_flags, name='Retro: Model Draw Flags', default='RETRO_ALPHA')\n" "# Clear Scene\n" "for ob in bpy.data.objects:\n" " if ob.type != 'CAMERA':\n" " bpy.context.scene.objects.unlink(ob)\n" " bpy.data.objects.remove(ob)\n" "\n" "def duplicateObject(copy_obj):\n" " # Create new mesh\n" " mesh = bpy.data.meshes.new(copy_obj.name)\n" " # Create new object associated with the mesh\n" " ob_new = bpy.data.objects.new(copy_obj.name, mesh)\n" " # Copy data block from the old object into the new object\n" " ob_new.data = copy_obj.data\n" " ob_new.scale = copy_obj.scale\n" " ob_new.location = copy_obj.location\n" " # Link new object to the given scene and select it\n" " bpy.context.scene.objects.link(ob_new)\n" " return ob_new\n"; os.format("bpy.context.scene.name = '%s'\n" "bpy.context.scene.render.resolution_x = 640\n" "bpy.context.scene.render.resolution_y = 480\n" "bpy.context.scene.render.engine = 'CYCLES'\n" "bpy.context.scene.world.use_nodes = True\n" "bpy.context.scene.render.engine = 'BLENDER_GAME'\n" "bg_node = bpy.context.scene.world.node_tree.nodes['Background']\n", pakRouter.getBestEntryName(entry).c_str()); for (const FRME::Widget& w : frme.widgets) { os << "binding = None\n" "angle = Quaternion((0.0, 0.0, 0.0), 0)\n"; if (w.type == SBIG('CAMR')) { using CAMRInfo = Widget::CAMRInfo; os.format("cam = bpy.data.cameras.new(name='%s')\n" "binding = cam\n", w.header.name.c_str()); CAMRInfo* info = static_cast(w.widgetInfo.get()); if (info) { if (info->projectionType == CAMRInfo::ProjectionType::Orthographic) { CAMRInfo::OrthographicProjection* proj = static_cast(info->projection.get()); os.format("cam.type = 'ORTHO'\n" "cam.ortho_scale = %f\n", std::fabs(proj->right - proj->left)); } else if (info->projectionType == CAMRInfo::ProjectionType::Perspective) { CAMRInfo::PerspectiveProjection* proj = static_cast(info->projection.get()); os.format("cam.type = 'PERSP'\n" "cam.lens_unit = 'FOV'\n" "cam.angle = math.radians(%f)\n" "cam.clip_start = %f\n" "cam.clip_end = %f\n" "bpy.context.scene.render.pixel_aspect_x = %f\n" "bpy.context.scene.render.pixel_aspect_y = bpy.context.scene.render.pixel_aspect_x\n", proj->fov, proj->znear, proj->zfar, proj->aspect); } } os << "angle = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))\n"; } else if (w.type == SBIG('LITE')) { using LITEInfo = Widget::LITEInfo; LITEInfo* info = static_cast(w.widgetInfo.get()); if (info) { switch(info->type) { case LITEInfo::ELightType::LocalAmbient: os.format("bg_node.inputs[0].default_value = (%f,%f,%f,1.0\n" "bg_node.inputs[1].default_value = %f\n", w.header.color.vec[0], w.header.color.vec[1], w.header.color.vec[2], info->distQ / 8.0); default: os.format("lamp = bpy.data.lamps.new(name='%s', type='POINT')\n" "lamp.color = (%f, %f, %f)\n" "binding = lamp\n", w.header.name.c_str(), w.header.color.vec[0], w.header.color.vec[1], w.header.color.vec[2]); } } } os.format("frme_obj = bpy.data.objects.new(name='%s', object_data=binding)\n" "parentName = '%s'\n" "frme_obj.retro_widget_type = '%s'\n" "frme_obj.retro_widget_use_anim_controller = %s\n" "frme_obj.retro_widget_default_visible = %s\n" "frme_obj.retro_widget_default_active = %s\n" "frme_obj.retro_widget_cull_faces = %s\n" "frme_obj.retro_widget_color = (%f,%f,%f,%f)\n" "frme_obj.retro_widget_model_draw_flags = model_draw_flags[%i][0]\n" "if parentName not in bpy.data.objects:\n" " frme_obj.retro_widget_parent = parentName\n" "else:\n" " frme_obj.parent = bpy.data.objects[parentName]\n", w.header.name.c_str(), w.header.parent.c_str(), w.type.toString().c_str(), w.header.useAnimController ? "True" : "False", w.header.defaultVisible ? "True" : "False", w.header.defaultActive ? "True" : "False", w.header.cullFaces ? "True" : "False", w.header.color.vec[0], w.header.color.vec[1], w.header.color.vec[2], w.header.color.vec[3], w.header.modelDrawFlags); if (w.type == SBIG('MODL')) { using MODLInfo = FRME::Widget::MODLInfo; MODLInfo* info = dynamic_cast(w.widgetInfo.get()); hecl::ProjectPath modelPath = pakRouter.getWorking(info->model); const PAKRouter::EntryType* cmdlE = pakRouter.lookupEntry(info->model, nullptr, true, true); os.linkBlend(modelPath.getAbsolutePathUTF8().c_str(), pakRouter.getBestEntryName(*cmdlE).c_str(), true); os << "print(obj.name)\n" "copy_obj = duplicateObject(obj)\n" "copy_obj.parent = frme_obj\n" "copy_obj.hide = False\n"; } else if (w.type == SBIG('IMGP')) { using IMGPInfo = Widget::IMGPInfo; IMGPInfo* info = dynamic_cast(w.widgetInfo.get()); if (info && info->texture) { std::string texName = pakRouter.getBestEntryName(info->texture); const nod::Node* node; const PAKRouter::EntryType* texEntry = pakRouter.lookupEntry(info->texture, &node); hecl::ProjectPath txtrPath = pakRouter.getWorking(texEntry); if (txtrPath.isNone()) { PAKEntryReadStream rs = texEntry->beginReadStream(*node); TXTR::Extract(rs, txtrPath); } hecl::SystemString resPath = pakRouter.getResourceRelativePath(entry, info->texture); hecl::SystemUTF8View resPathView(resPath); os.format("if '%s' in bpy.data.images:\n" " image = bpy.data.images['%s']\n" "else:\n" " image = bpy.data.images.load('''//%s''')\n" " image.name = '%s'\n" "frme_obj.empty_draw_type = 'IMAGE'\n" "frme_obj.data = image\n" "angle = Quaternion((1.0, 0.0, 0.0), math.radians(90.0))\n", texName.c_str(), texName.c_str(), resPathView.str().c_str(), texName.c_str()); } } os.format("mtx = Matrix(((%f,%f,%f,%f),(%f,%f,%f,%f),(%f,%f,%f,%f),(0.0,0.0,0.0,1.0)))\n" "mtxd = mtx.decompose()\n" "frme_obj.rotation_mode = 'QUATERNION'\n" "frme_obj.location = mtxd[0]\n" "frme_obj.rotation_quaternion = mtxd[1] * angle\n" "frme_obj.scale = mtxd[2]\n" "bpy.context.scene.objects.link(frme_obj)\n", w.basis[0].vec[0], w.basis[0].vec[1], w.basis[0].vec[2],w.origin.vec[0], w.basis[1].vec[0], w.basis[1].vec[1], w.basis[1].vec[2],w.origin.vec[1], w.basis[2].vec[0], w.basis[2].vec[1], w.basis[2].vec[2],w.origin.vec[2]); } os.centerView(); os.close(); conn.saveBlend(); return true; } } }