//------------------------------------------------------------------------------ //! bool ParticleGeometry::updateRenderData() { CHECK( _rgeom.isValid() ); CHECK( _rgeom->numBuffers() >= 1 ); CHECK( _entity ); const RCP<Gfx::VertexBuffer>& buffer = _rgeom->buffers()[0]; const ParticleData& data = _entity->data(); //data.print(); //getchar(); size_t s = data.size() * data.vertexStride() * sizeof(float); float* dst = (float*)Core::gfx()->map( buffer, Gfx::MAP_WRITE, 0, s ); if( dst ) { memcpy( dst, data.vertexData(), s ); Core::gfx()->unmap( buffer ); clearPatches(); addPatch( 0, data.size(), 0 ); return true; } else { CHECK( dst != NULL ); return false; } }
USING_NAMESPACE /*============================================================================== UNNAMED NAMESPACE ==============================================================================*/ UNNAMESPACE_BEGIN UNNAMESPACE_END /*============================================================================== CLASS ParticleGeometry ==============================================================================*/ //------------------------------------------------------------------------------ //! ParticleGeometry::ParticleGeometry( ParticleEntity* e ): Geometry( Geometry::PARTICLE ), _entity( e ) { // Fake a patch to avoid culling in Renderer::classifyEntities(). addPatch( 0, 0, 0 ); }
void TextureLumpArea::onAddPatch(wxCommandEvent &event) { if (list_textures->GetSelection() == -1 || list_pnames->GetSelection() == -1) return; addPatch(list_pnames->GetSelection()); }
// ----------------------------------------------------------------------------- // Loads a PNAMES entry, returns true on success, false otherwise // ----------------------------------------------------------------------------- bool PatchTable::loadPNAMES(ArchiveEntry* pnames, Archive* parent) { // Check entry was given if (!pnames) return false; // Mute while loading setMuted(true); // Clear current table patches_.clear(); // Setup parent archive if (!parent) parent = pnames->parent(); // Read number of pnames uint32_t n_pnames = 0; pnames->seek(0, SEEK_SET); if (!pnames->read(&n_pnames, 4)) { Log::error("PNAMES lump is corrupt"); return false; } // Read pnames content for (uint32_t a = 0; a < n_pnames; a++) { char pname[9] = ""; pname[8] = 0; // Try to read pname if (!pnames->read(&pname, 8)) { Log::error("PNAMES entry {} is corrupt", a); return false; } // Add new patch addPatch(StrUtil::upper(pname), true); } // Update variables parent_ = parent; setMuted(false); // Announce announce("modified"); return true; }
/* TextureEditorPanel::handleAction * Handles the action [id]. Returns true if the action was handled, * false otherwise *******************************************************************/ bool TextureEditorPanel::handleAction(string id) { // Don't handle actions if hidden if (!IsShown()) return false; // Only interested in actions beginning with txed_ if (!id.StartsWith("txed_")) return false; // Add Patch if (id == "txed_patch_add") addPatch(); // Remove Patch else if (id == "txed_patch_remove") removePatch(); // Send Patch Back else if (id == "txed_patch_back") patchBack(); // Bring Patch Forward else if (id == "txed_patch_forward") patchForward(); // Replace Patch else if (id == "txed_patch_replace") replacePatch(); // Duplicate Patch else if (id == "txed_patch_duplicate") duplicatePatch(); // Unknown action else return false; // Action was handled return true; }
/* CTexture::copyTexture * Copies the texture [tex] to this texture. If keep_type is true, * the current texture type (extended/regular) will be kept, * otherwise it will be converted to the type of [tex] *******************************************************************/ void CTexture::copyTexture(CTexture* tex, bool keep_type) { // Check texture was given if (!tex) return; // Clear current texture clear(); // Copy texture info this->name = tex->name; this->width = tex->width; this->height = tex->height; this->scale_x = tex->scale_x; this->scale_y = tex->scale_y; this->world_panning = tex->world_panning; if (!keep_type) this->extended = tex->extended; this->optional = tex->optional; this->no_decals = tex->no_decals; this->null_texture = tex->null_texture; this->offset_x = tex->offset_x; this->offset_y = tex->offset_y; this->type = tex->type; // Copy patches for (unsigned a = 0; a < tex->nPatches(); a++) { CTPatch* patch = tex->getPatch(a); if (extended) { if (tex->extended) patches.push_back(new CTPatchEx((CTPatchEx*)patch)); else patches.push_back(new CTPatchEx(patch)); } else addPatch(patch->getName(), patch->xOffset(), patch->yOffset()); } }
/* TextureEditorPanel::onTexCanvasKeyDown * Called when a key is pressed within the texture canvas *******************************************************************/ void TextureEditorPanel::onTexCanvasKeyDown(wxKeyEvent& e) { // Check if keypress matches any keybinds wxArrayString binds = KeyBind::getBinds(KeyBind::asKeyPress(e.GetKeyCode(), e.GetModifiers())); // Check for alt key if (e.GetKeyCode() == WXK_ALT) alt_press = true; // Go through matching binds int x_movement = 0; int y_movement = 0; bool handled = false; for (unsigned a = 0; a < binds.size(); a++) { string name = binds[a]; // Move patch left if (name == "txed_patch_left") x_movement = -1; else if (name == "txed_patch_left8") x_movement = -8; // Move patch up else if (name == "txed_patch_up") y_movement = -1; else if (name == "txed_patch_up8") y_movement = -8; // Move patch right else if (name == "txed_patch_right") x_movement = 1; else if (name == "txed_patch_right8") x_movement = 8; // Move patch down else if (name == "txed_patch_down") y_movement = 1; else if (name == "txed_patch_down8") y_movement = 8; // Add patch else if (name == "txed_patch_add") { hack_nodrag = true; addPatch(); handled = true; } // Delete patch else if (name == "txed_patch_delete") { removePatch(); handled = true; } // Replace patch else if (name == "txed_patch_replace") { hack_nodrag = true; replacePatch(); handled = true; } // Duplicate patch else if (name == "txed_patch_duplicate") { duplicatePatch(); handled = true; } // Bring patch forward else if (name == "txed_patch_forward") { patchForward(); handled = true; } // Send patch back else if (name == "txed_patch_back") { patchBack(); handled = true; } } // Move patches if needed if (x_movement != 0 || y_movement != 0) { // Do patch duplicate if alt is pressed if (e.GetModifiers() == wxMOD_ALT && alt_press) { duplicatePatch(0, 0); alt_press = false; } wxArrayInt selected_patches = list_patches->selectedItems(); for (size_t a = 0; a < selected_patches.size(); a++) { CTPatch* patch = tex_current->getPatch(selected_patches[a]); if (!patch) continue; int16_t cx = patch->xOffset(); int16_t cy = patch->yOffset(); patch->setOffsetX(cx + x_movement); patch->setOffsetY(cy + y_movement); tex_modified = true; } tex_canvas->redraw(true); handled = true; } if (!e.AltDown()) alt_press = false; if (!handled) e.Skip(); }
/* TextureEditorPanel::onBtnPatchAdd * Called when the 'add patch' button is pressed *******************************************************************/ void TextureEditorPanel::onBtnPatchAdd(wxCommandEvent& e) { addPatch(); }
void Rig::loadPatches(JSONNode root) { JSONNode::iterator i = root.begin(); // for this we want to iterate through all children and have the device class // parse the sub-element. while (i != root.end()){ if (i->name() == "jsonPath") { i++; continue; } // get the node name and value as a string std::string nodeName = i->name(); stringstream ss; ss << "Loading patch " << nodeName; Logger::log(INFO, ss.str()); Patch* patch; auto type = i->find("type"); if (type == i->end()) { stringstream ss; ss << "Unable to determine Patch type for " << nodeName << ". Patch not loaded."; Logger::log(WARN, ss.str()); } string patchType = type->as_string(); // New patch types will need new seralization definitions. if (patchType == "DMXPatch") { patch = (Patch*) new DMXPatch(*i); addPatch(nodeName, patch); } #ifdef USE_ARNOLD else if (patchType == "PhotoPatch") { patch = (Patch*) new PhotoPatch(*i); addPatch(nodeName, patch); Device::DeviceCallbackFunction callback = std::bind(&PhotoPatch::onDeviceChanged, (PhotoPatch*)patch, std::placeholders::_1); for (Device *d : getDeviceRaw()) { d->addParameterChangedCallback(callback); d->addMetadataChangedCallback(callback); } } else if (patchType == "PhotoAnimationPatch") { patch = (Patch*) new PhotoAnimationPatch(*i); addPatch(nodeName, patch); Device::DeviceCallbackFunction callback = std::bind(&PhotoAnimationPatch::onDeviceChanged, (PhotoAnimationPatch*)patch, std::placeholders::_1); for (Device *d : getDeviceRaw()) { d->addParameterChangedCallback(callback); d->addMetadataChangedCallback(callback); } } else if (patchType == "ArnoldAnimationPatch") { i->push_back(*root.find("jsonPath")); patch = (Patch*) new ArnoldAnimationPatch(*i); addPatch(nodeName, patch); Device::DeviceCallbackFunction callback = std::bind(&ArnoldAnimationPatch::onDeviceChanged, (ArnoldAnimationPatch*)patch, std::placeholders::_1); for (Device *d : getDeviceRaw()) { d->addParameterChangedCallback(callback); d->addMetadataChangedCallback(callback); } } else if (patchType == "ArnoldPatch") { i->push_back(*i->find("jsonPath")); patch = (Patch*) new ArnoldPatch(*i); addPatch(nodeName, patch); Device::DeviceCallbackFunction callback = std::bind(&ArnoldPatch::onDeviceChanged, (ArnoldPatch*)patch, std::placeholders::_1); for (Device *d : getDeviceRaw()) { d->addParameterChangedCallback(callback); d->addMetadataChangedCallback(callback); } } #endif else { stringstream ss; ss << "Unknown Patch type " << patchType << " in Patch ID " << nodeName << "Patch not loaded."; Logger::log(WARN, ss.str()); } //increment the iterator ++i; } }
// ----------------------------------------------------------------------------- // Static function to check if an archive has sufficient texture related // entries, and if not, prompts the user to either create or import them. // Returns true if the entries exist, false otherwise // ----------------------------------------------------------------------------- bool TextureXEditor::setupTextureEntries(Archive* archive) { using Format = TextureXList::Format; // Check any archive was given if (!archive) return false; // Search archive for any ZDoom TEXTURES entries Archive::SearchOptions options; options.match_type = EntryType::fromId("zdtextures"); auto entry_tx = archive->findFirst(options); // Find any TEXTURES entry // If it's found, we're done if (entry_tx) return true; // Search archive for any texture-related entries options.match_type = EntryType::fromId("texturex"); entry_tx = archive->findFirst(options); // Find any TEXTUREx entry options.match_type = EntryType::fromId("pnames"); auto entry_pnames = archive->findFirst(options); // Find any PNAMES entry // If both exist, we're done if (entry_tx && entry_pnames) return true; // Todo: accept entry_tx without pnames if the textures are in Jaguar mode // If no TEXTUREx entry exists if (!entry_tx) { // No TEXTUREx entries found, so ask if the user wishes to create one wxMessageDialog dlg( nullptr, "The archive does not contain any texture definitions (TEXTURE1/2 or TEXTURES). " "Do you wish to create or import a texture definition list?", "No Texture Definitions Found", wxYES_NO); if (dlg.ShowModal() == wxID_YES) { CreateTextureXDialog ctxd(nullptr); while (true) { // Check if cancelled if (ctxd.ShowModal() == wxID_CANCEL) return false; if (ctxd.createNewSelected()) { // User selected to create a new TEXTUREx list ArchiveEntry* texturex = nullptr; // Doom or Strife TEXTUREx if (ctxd.getSelectedFormat() == Format::Normal || ctxd.getSelectedFormat() == Format::Strife11) { // Create texture list TextureXList txlist; txlist.setFormat(ctxd.getSelectedFormat()); // Create patch table PatchTable ptt; // Create dummy patch auto dpatch = App::archiveManager().programResourceArchive()->entryAtPath("s3dummy.lmp"); archive->addEntry(dpatch, "patches", true); ptt.addPatch("S3DUMMY"); // Create dummy texture auto dummytex = std::make_unique<CTexture>(); dummytex->setName("S3DUMMY"); dummytex->addPatch("S3DUMMY", 0, 0); dummytex->setWidth(128); dummytex->setHeight(128); dummytex->setScale({ 0., 0. }); // Add dummy texture to list // (this serves two purposes - supplies the special 'invalid' texture by default, // and allows the texturex format to be detected) txlist.addTexture(std::move(dummytex)); // Add empty PNAMES entry to archive entry_pnames = archive->addNewEntry("PNAMES"); ptt.writePNAMES(entry_pnames); entry_pnames->setType(EntryType::fromId("pnames")); entry_pnames->setExtensionByType(); // Add empty TEXTURE1 entry to archive texturex = archive->addNewEntry("TEXTURE1"); txlist.writeTEXTUREXData(texturex, ptt); texturex->setType(EntryType::fromId("texturex")); texturex->setExtensionByType(); } else if (ctxd.getSelectedFormat() == Format::Textures) { // Create texture list TextureXList txlist; txlist.setFormat(Format::Textures); // Add empty TEXTURES entry to archive texturex = archive->addNewEntry("TEXTURES"); texturex->setType(EntryType::fromId("zdtextures")); texturex->setExtensionByType(); return false; } if (!texturex) return false; } else { // User selected to import texture definitions from the base resource archive auto bra = App::archiveManager().baseResourceArchive(); if (!bra) { wxMessageBox( "No Base Resource Archive is opened, please select/open one", "Error", wxICON_ERROR); continue; } // Find all relevant entries in the base resource archive Archive::SearchOptions opt; opt.match_type = EntryType::fromId("texturex"); auto import_tx = bra->findAll(opt); // Find all TEXTUREx entries opt.match_type = EntryType::fromId("pnames"); auto import_pnames = bra->findLast(opt); // Find last PNAMES entry // Check enough entries exist if (import_tx.empty() || !import_pnames) { wxMessageBox( "The selected Base Resource Archive does not contain " "sufficient texture definition entries", "Error", wxICON_ERROR); continue; } // Copy TEXTUREx entries over to current archive for (auto& a : import_tx) { auto texturex = archive->addEntry(a, "global", true); texturex->setType(EntryType::fromId("texturex")); texturex->setExtensionByType(); } // Copy PNAMES entry over to current archive entry_pnames = archive->addEntry(import_pnames, "global", true); entry_pnames->setType(EntryType::fromId("pnames")); entry_pnames->setExtensionByType(); } break; } return true; } // 'No' clicked return false; } else // TEXTUREx entry exists { // TODO: Probably a better idea here to get the user to select an archive to import the patch table from // If no PNAMES entry was found, search resource archives if (!entry_pnames) { Archive::SearchOptions opt; opt.match_type = EntryType::fromId("pnames"); entry_pnames = App::archiveManager().findResourceEntry(opt, archive); } // If no PNAMES entry is found at all, show an error and abort // TODO: ask user to select appropriate base resource archive if (!entry_pnames) { wxMessageBox("PNAMES entry not found!", wxMessageBoxCaptionStr, wxICON_ERROR); return false; } return true; } return false; }
// ----------------------------------------------------------------------------- // Reads in a doom-format TEXTUREx entry. // Returns true on success, false otherwise // ----------------------------------------------------------------------------- bool TextureXList::readTEXTUREXData(ArchiveEntry* texturex, const PatchTable& patch_table, bool add) { // Check entries were actually given if (!texturex) return false; // Clear current textures if needed if (!add) clear(); // Update palette MainEditor::setGlobalPaletteFromArchive(texturex->parent()); // Read TEXTUREx // Read header texturex->seek(0, SEEK_SET); // Number of textures int32_t n_tex = 0; if (!texturex->read(&n_tex, 4)) { Log::error("TEXTUREx entry is corrupt (can't read texture count)"); return false; } n_tex = wxINT32_SWAP_ON_BE(n_tex); // If it's an empty TEXTUREx entry, stop here if (n_tex == 0) return true; // Texture definition offsets vector<int32_t> offsets(n_tex); if (!texturex->read(offsets.data(), n_tex * 4)) { Log::error("TEXTUREx entry is corrupt (can't read first offset)"); return false; } // Read the first texture definition to try to identify the format if (!texturex->seek(wxINT32_SWAP_ON_BE(offsets[0]), SEEK_SET)) { Log::error("TEXTUREx entry is corrupt (can't read first definition)"); return false; } // Look at the name field. Is it present or not? char tempname[8]; if (!texturex->read(&tempname, 8)) { Log::error("TEXTUREx entry is corrupt (can't read first name)"); return false; } // Let's pretend it is and see what happens. txformat_ = Format::Normal; // Only the characters A-Z (uppercase), 0-9, and [ ] - _ should be used in texture names. for (uint8_t a = 0; a < 8; ++a) { if (a > 0 && tempname[a] == 0) // We found a null-terminator for the string, so we can assume it's okay. break; if (tempname[a] >= 'a' && tempname[a] <= 'z') { txformat_ = Format::Jaguar; // Log::info(1, "Jaguar texture"); break; } else if (!((tempname[a] >= 'A' && tempname[a] <= '[') || (tempname[a] >= '0' && tempname[a] <= '9') || tempname[a] == ']' || tempname[a] == '-' || tempname[a] == '_')) // We're out of character range, so this is probably not a texture name. { txformat_ = Format::Nameless; // Log::info(1, "Nameless texture"); break; } } // Now let's see if it is the abridged Strife format or not. if (txformat_ == Format::Normal) { // No need to test this again since it was already tested before. texturex->seek(offsets[0], SEEK_SET); FullTexDef temp; if (!texturex->read(&temp, 22)) { Log::error("TEXTUREx entry is corrupt (can't test definition)"); return false; } // Test condition adapted from ZDoom; apparently the first two bytes of columndir // may be set to garbage values by some editors and are therefore unreliable. if (wxINT16_SWAP_ON_BE(temp.patchcount <= 0) || (temp.columndir[1] != 0)) txformat_ = Format::Strife11; } // Read all texture definitions for (int32_t a = 0; a < n_tex; a++) { // Skip to texture definition if (!texturex->seek(offsets[a], SEEK_SET)) { Log::error("TEXTUREx entry is corrupt (can't find definition)"); return false; } // Read definition TexDef tdef; if (txformat_ == Format::Nameless) { NamelessTexDef nameless; // Auto-naming mechanism taken from DeuTex if (a > 99999) { Log::error("More than 100000 nameless textures"); return false; } char temp[9] = ""; sprintf(temp, "TEX%05d", a); memcpy(tdef.name, temp, 8); // Read texture info if (!texturex->read(&nameless, 8)) { Log::error("TEXTUREx entry is corrupt (can't read nameless definition #{})", a); return false; } // Copy data to permanent structure tdef.flags = nameless.flags; tdef.scale[0] = nameless.scale[0]; tdef.scale[1] = nameless.scale[1]; tdef.width = nameless.width; tdef.height = nameless.height; } else if (!texturex->read(&tdef, 16)) { Log::error("TEXTUREx entry is corrupt, (can't read texture definition #{})", a); return false; } // Skip unused if (txformat_ != Format::Strife11) { if (!texturex->seek(4, SEEK_CUR)) { Log::error("TEXTUREx entry is corrupt (can't skip dummy data past #{})", a); return false; } } // Create texture tdef.cleanupName(); auto tex = std::make_unique<CTexture>(); tex->name_ = StrUtil::viewFromChars(tdef.name, 8); tex->size_.x = wxINT16_SWAP_ON_BE(tdef.width); tex->size_.y = wxINT16_SWAP_ON_BE(tdef.height); tex->scale_.x = tdef.scale[0] / 8.0; tex->scale_.y = tdef.scale[1] / 8.0; // Set flags if (tdef.flags & Flags::WorldPanning) tex->world_panning_ = true; // Read patches int16_t n_patches = 0; if (!texturex->read(&n_patches, 2)) { Log::error("TEXTUREx entry is corrupt (can't read patchcount #{})", a); return false; } // Log::info(1, "Texture #{}: {} patch%s", a, n_patches, n_patches == 1 ? "" : "es"); for (uint16_t p = 0; p < n_patches; p++) { // Read patch definition Patch pdef; if (!texturex->read(&pdef, 6)) { Log::error("TEXTUREx entry is corrupt (can't read patch definition #{}:{})", a, p); Log::error("Lump size {}, offset {}", texturex->size(), texturex->currentPos()); return false; } // Skip unused if (txformat_ != Format::Strife11) { if (!texturex->seek(4, SEEK_CUR)) { Log::error("TEXTUREx entry is corrupt (can't skip dummy data past #{}:{})", a, p); return false; } } // Add it to the texture std::string patch; if (txformat_ == Format::Jaguar) { patch = StrUtil::upper(tex->name_); } else { patch = patch_table.patchName(pdef.patch); } if (patch.empty()) { // Log::info(1, "Warning: Texture %s contains patch %d which is invalid - may be incorrect PNAMES // entry", tex->getName(), pdef.patch); patch = fmt::format("INVPATCH{:04d}", pdef.patch); } tex->addPatch(patch, pdef.left, pdef.top); } // Add texture to list addTexture(std::move(tex)); } return true; }