Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
410 changes: 410 additions & 0 deletions docs/MODEL_FRAMEWORK.md

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions docs/PC.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ key is optional; these are the defaults:
"detail_distance": 0, // how far full detail reaches (world units); 0: at any distance
"shadow_distance": 0, // how far town parts cast full shadows (world units); 0: at any distance
"anisotropy": 0, // anisotropic filtering of 3D textures: 0 (off), 2, 4, 8 or 16 samples
"soft_focus": true // the game's farside soft focus
"soft_focus": true, // the game's farside soft focus
"cloth": "original" // original: the PS2's cloth; enhanced: also kept out of the body (docs/MODEL_FRAMEWORK.md)
},
"audio": {
"master_volume": 1.0, // 0 to 1
Expand Down Expand Up @@ -1394,7 +1395,7 @@ help. Retail's screen-position row is gone: `MGAdjustScreen` moves nothing on PC
| Page | Rows |
|---|---|
| Game | save cursor position, clock, time speed, dungeon map, enemy damage, party damage, enemy HP, names, Discord Rich Presence, element quick select, swap keeps lock-on, autosave |
| Display | window mode, resolution (the monitor's own and the sizes that fit it), V-Sync (`fifo`, `mailbox`, `immediate`), frame limit, aspect ratio, interface size (`ui_scale`), smooth motion (`interpolation`), FPS counter, FPS info (`fps_detail`), anisotropic filter (`anisotropy`), soft focus |
| Display | window mode, resolution (the monitor's own and the sizes that fit it), V-Sync (`fifo`, `mailbox`, `immediate`), frame limit, aspect ratio, interface size (`ui_scale`), smooth motion (`interpolation`), FPS counter, FPS info (`fps_detail`), anisotropic filter (`anisotropy`), soft focus, cloth (`cloth`) |
| Audio | volume, sound (mono, stereo or surround), soundtrack (PS2 or custom) |
| Controls | vibration, mouse sensitivity (in hundredths below 1 and tenths above, whatever its unit), invert mouse Y, vertical return, mouse wheel zoom, reset zoom (its binding), stick sensitivity, invert stick X and Y, gyro, gyro sensitivity, invert gyro X and Y |
| Text | language, message speed, button symbols (original or new), symbols shown (glyph device), text font (sharp or original), text shadow, symbol shadow, area name shadow, floor label shadow, boss name shadow |
Expand Down Expand Up @@ -1952,6 +1953,12 @@ first. `list` prints each file's sector, size and path. `publish-sound`
converts an extracted folder's sound packs into ChronicleTwo's bank, sample
and sequence formats beside the originals, which the game still plays
(`docs/AUDIO_FRAMEWORK.md`).
`extract` also converts the models, motions, skin weights and bind poses to
glTF beside the originals, indexed by `models.json`; the game rebuilds the
retail records from them as it reads a file. `convert-models <raw> <out>`
writes that conversion to a folder of its own, `bake-assets <raw> <data>`
publishes it into extracted data, and `strip-models <data>` empties the
converted originals (`docs/MODEL_FRAMEWORK.md`).

The layout of `data/` or the root of `data.zip` is the disc archive's own: `dun/pack/maindat.pac`,
`commenu/a_eng/savetex.pak`, `sound/bgm/...` and so on. Lookups fold case,
Expand Down
2 changes: 1 addition & 1 deletion port/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ add_library(dc_port OBJECT ${PORT_LIB_SOURCES} ${DC_SCE_SOURCES})
if(NINTENDO_SWITCH)
dc_switch_port(dc_port)
endif()
target_link_libraries(dc_port PRIVATE dc_options SDL3::SDL3 Vulkan::Vulkan nlohmann_json::nlohmann_json kz_port_headers)
target_link_libraries(dc_port PRIVATE dc_options SDL3::SDL3 Vulkan::Vulkan nlohmann_json::nlohmann_json fastgltf::fastgltf kz_port_headers)
# port/src, the generated headers and the stb directories: captionfont.cpp rasterizes with
# stb_truetype and platform/png_image.cpp decodes with stb_image, which Kazarov stages (or fetches)
# for dc_host.
Expand Down
71 changes: 71 additions & 0 deletions port/include/game/cloth.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

#include <libvu0.h>

#include <array>
#include <cstdint>
#include <span>
#include <vector>

#include "dataalloc.hpp"
#include "mdt.hpp"
#include "visualvu1.hpp"
Expand Down Expand Up @@ -104,4 +109,70 @@ class CCloth : public CVisualVu1 {
virtual int DrawVu1(sceVif1Packet *packet, float (*matrix)[4], RenderInfo *info, VU1_PROGRAM program, u_long128 *draw_state, int unknown1, int unknown2);
};

/**
* The Options screen's Enhanced cloth pushes the cloth out of the body as well as the .clo files'
* boxes. The body is the skeleton as capsules.
*/
namespace cloth_body {

constexpr int kTorsoSectors = 12; /**< How many directions round its axis a torso's shape is measured in. */
constexpr int kTorsoBands = 6; /**< How many heights along its axis a torso's shape is measured at. */

/**
* A capsule of the body, from a to b, radius thick.
*/
struct Capsule {
std::array<float, 3> a;
std::array<float, 3> b;
float radius;
bool torso = false; /**< Keeps the cloth off itself, and stops the limbs pressing the cloth into it. */
/**
* A torso fitted to the body's mesh (FitTorsos): how far its surface lies from the axis, by direction
* round it (measured from `side`, a direction square to the axis that turns with the body) and by
* height from a to b. Unfitted, the torso is round, `radius` thick.
*/
std::array<float, 3> side = {};
std::array<float, kTorsoSectors * kTorsoBands> shape = {};
bool fitted = false;
};

/**
* How far the capsule's surface lies from its axis in the direction of `p`.
*/
float Reach(const Capsule &capsule, const float *p);

/**
* The body's mesh: its vertices, in world space, and its triangles, three vertices each.
*/
struct Mesh {
std::vector<std::array<float, 3>> vertices;
std::vector<std::array<std::uint32_t, 3>> triangles;
};

/**
* Fits the torsos among `capsules` to `body` as it stands: how far out from the axis each direction meets
* the torso's faces. A vertex belongs to the capsule whose surface it lies nearest, `others` (the head,
* the hands, the feet) among them, so arms and hands held against the torso don't fatten it. A round
* capsule sized from the spine's length stood out behind Toan's back, his chest being deeper than it.
*/
void FitTorsos(std::span<Capsule> capsules, std::span<const Capsule> others, const Mesh &body);

/**
* How near a limb's axis a vertex may come: the limb's thickness, or less where the cloth rests against
* the limb, as near as `home` (where the cloth's rest shape puts the vertex when it settles on the body)
* lies to it then. A vertex resting against an arm isn't pushed off it, and an arm raised through where
* the cloth rests still pushes it.
*/
float Clearance(const Capsule &limb, const float *home);

/**
* Pushes a free vertex out of `capsules`, each limb to its `clearances` entry (Clearance, taken when the
* cloth settled), the torso to its surface, or as far out as `home` lies if that is inside it. A limb
* pressing the vertex towards the torso stops it at the torso's surface, or where it rests if that is
* nearer.
*/
void PushOutOfBody(std::span<const Capsule> capsules, std::span<const float> clearances, const float *home, float *vertex);

} // namespace cloth_body

} // namespace dc::game
4 changes: 4 additions & 0 deletions port/lang/de_de.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
"options.video.soft_focus.label": "Weichzeichner",
"options.video.soft_focus.choice.0": "An",
"options.video.soft_focus.choice.1": "Aus",
"options.video.cloth.label": "Stoff",
"options.video.cloth.help": "\"Stoff\"\nOriginal: wie auf der PS2.\nVerbessert: der Körper\ngeht nicht mehr hindurch.",
"options.video.cloth.choice.0": "Original",
"options.video.cloth.choice.1": "Verbessert",
"options.page.audio": "Audio",
"options.audio.master_volume.label": "Lautstärke",
"options.audio.master_volume.help": "\"Lautstärke\"\nWie laut das Spiel ist.",
Expand Down
4 changes: 4 additions & 0 deletions port/lang/es_es.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
"options.video.soft_focus.label": "Enfoque suave",
"options.video.soft_focus.choice.0": "Sí",
"options.video.soft_focus.choice.1": "No",
"options.video.cloth.label": "Tela",
"options.video.cloth.help": "\"Tela\"\nOriginal: la de PS2.\nMejorada: el cuerpo\nya no la atraviesa.",
"options.video.cloth.choice.0": "Original",
"options.video.cloth.choice.1": "Mejorada",
"options.page.audio": "Audio",
"options.audio.master_volume.label": "Volumen",
"options.audio.master_volume.help": "\"Volumen\"\nQué tan alto suena.",
Expand Down
4 changes: 4 additions & 0 deletions port/lang/fr_fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
"options.video.soft_focus.label": "Flou doux",
"options.video.soft_focus.choice.0": "Activé",
"options.video.soft_focus.choice.1": "Désactivé",
"options.video.cloth.label": "Tissus",
"options.video.cloth.help": "\"Tissus\"\nOriginal: celui de la PS2.\nAmélioré: le corps\nne le traverse plus.",
"options.video.cloth.choice.0": "Original",
"options.video.cloth.choice.1": "Amélioré",
"options.page.audio": "Audio",
"options.audio.master_volume.label": "Volume",
"options.audio.master_volume.help": "\"Volume\"\nLe volume du jeu.",
Expand Down
4 changes: 4 additions & 0 deletions port/lang/it_it.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
"options.video.soft_focus.label": "Fuoco morbido",
"options.video.soft_focus.choice.0": "Sì",
"options.video.soft_focus.choice.1": "No",
"options.video.cloth.label": "Tessuti",
"options.video.cloth.help": "\"Tessuti\"\nOriginale: come su PS2.\nMigliorato: il corpo\nnon li attraversa più.",
"options.video.cloth.choice.0": "Originale",
"options.video.cloth.choice.1": "Migliorato",
"options.page.audio": "Audio",
"options.audio.master_volume.label": "Volume",
"options.audio.master_volume.help": "\"Volume\"\nQuanto forte è il\ngioco.",
Expand Down
13 changes: 13 additions & 0 deletions port/src/data/data_read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <vector>

#include "../../../tools/dcdata/dcdata.hpp"
#include "data/model_gltf.hpp"
#include "dataalloc.hpp"
#include "platform/data_archive.hpp"
#include "platform/load_work_profile.hpp"
Expand Down Expand Up @@ -109,6 +110,7 @@ std::unordered_map<const void *, PackCapacity> loaded_capaci
std::unordered_map<const void *, std::unordered_set<const void *>> copied_capacities_by_owner;
constinit std::atomic<std::size_t> copied_capacity_count = 0;
bool data_indexed;
constinit std::atomic<std::uint64_t> data_generation = 0;
std::mutex texture_read_stats_mutex;
DataTextureReadStats texture_read_stats;
std::atomic<bool> texture_read_stats_enabled = false;
Expand Down Expand Up @@ -375,6 +377,7 @@ std::string IndexedPath(const IndexedFile &file) {
} // namespace

void DataReadIndex() {
data_generation.fetch_add(1, std::memory_order_relaxed);
ResetMetadataSnapshots();
const fs::path &root = PathsDataRoot();
std::error_code error;
Expand Down Expand Up @@ -502,16 +505,25 @@ bool DataReadLoadFile(std::string_view path, void *buffer, int *size) {
ForgetPackCapacities(buffer);
std::size_t capacity = 0;
*size = ReadWhole(*file, buffer, &capacity);
model_gltf::Splice(IndexedPath(*file), std::span(static_cast<std::uint8_t *>(buffer), static_cast<std::size_t>(*size)));
ForgetOverwrittenCapacities(buffer, capacity);
TrackPackCapacity(buffer, capacity, buffer, IndexedPath(*file), {}, 0, static_cast<std::size_t>(*size));
return true;
}

bool DataFileExists(std::string_view path) {
return Lookup(path) != nullptr;
}

std::uintmax_t DataFileSize(std::string_view path) {
const IndexedFile *file = Lookup(path);
return file != nullptr ? file->size : 0;
}

std::uint64_t DataReadGeneration() {
return data_generation.load(std::memory_order_relaxed);
}

bool DataReadIndexed() {
return data_indexed;
}
Expand Down Expand Up @@ -757,6 +769,7 @@ void DataReadForgetPackCapacities(const void *owner) {
}

void DataReadReset() {
model_gltf::Reset();
ResetMetadataSnapshots();
loaded_capacities.clear();
source_index.clear();
Expand Down
10 changes: 10 additions & 0 deletions port/src/data/data_read.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ std::vector<s32> SupportedLanguages();
*/
std::uintmax_t DataFileSize(std::string_view path);

/**
* Whether the game's data has a file of that name, even an empty one.
*/
bool DataFileExists(std::string_view path);

/**
* How many times the data has been indexed: what read it before can be stale once this changes.
*/
std::uint64_t DataReadGeneration();

/**
* Whether the data has been indexed. Unlike the readers it never indexes, which stops the program
* when there is no data.
Expand Down
Loading
Loading