Improved pathfinding logic
This commit is contained in:
1 parent
2659951d8e
commit
3a73f3fbde
36 files changed
+458337
-871
No files matched your search
+260
@@ -0,0 +1,260 @@
|
||||
# Pathfinding System
|
||||
|
||||
This document describes the grid-based pathfinding used for the player and all NPCs, including the collision avoidance and movement quality improvements.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Grid Representation](#1-grid-representation)
|
||||
2. [Building the Walkable Grid](#2-building-the-walkable-grid)
|
||||
3. [A\* Path Search](#3-a-path-search)
|
||||
4. [Path Smoothing](#4-path-smoothing)
|
||||
5. [Approaching Unreachable Destinations](#5-approaching-unreachable-destinations)
|
||||
6. [Dynamic Obstacles](#6-dynamic-obstacles)
|
||||
7. [Path Following](#7-path-following)
|
||||
8. [Character Collision Resolution](#8-character-collision-resolution)
|
||||
9. [Dynamic Replanning](#9-dynamic-replanning)
|
||||
10. [Key Constants Reference](#10-key-constants-reference)
|
||||
|
||||
---
|
||||
|
||||
## 1. Grid Representation
|
||||
|
||||
The world is divided into a uniform 2D grid in the XZ plane (Y is ignored during pathfinding; all characters walk on a flat floor at `floorY`).
|
||||
|
||||
Each cell is either **walkable** (`1`) or **blocked** (`0`). The grid is stored as a flat `std::vector<unsigned char>` indexed by `z * gridWidth + x`.
|
||||
|
||||
**Parameters** (all configurable in the JSON config file):
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|---|---|---|
|
||||
| `cellSize` | 0.4 m | Width and depth of one cell |
|
||||
| `agentRadius` | 0.45 m | Half-width of a character — used to erode free space |
|
||||
| `objectPadding` | 0.25 m | Extra clearance added around obstacle polygons |
|
||||
| `boundaryPadding` | 0.0 m | Inward erosion from the edges of navigation areas |
|
||||
| `floorY` | 0.0 | Y coordinate placed on every path waypoint |
|
||||
|
||||
**Grid bounds** are computed from the union of all navigation area polygons plus a padding margin of `cellSize * 2 + agentRadius + objectPadding` on every side.
|
||||
|
||||
**Cell coordinate conversion:**
|
||||
|
||||
```
|
||||
cell.x = floor((worldX - minX) / cellSize)
|
||||
cell.z = floor((worldZ - minZ) / cellSize)
|
||||
|
||||
cellCenter.x = minX + (cell.x + 0.5) * cellSize
|
||||
cellCenter.z = minZ + (cell.z + 0.5) * cellSize
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Building the Walkable Grid
|
||||
|
||||
The grid can be loaded in two ways.
|
||||
|
||||
### 2a. Pre-computed grid (`.txt` file)
|
||||
|
||||
A plain-text file with a small header followed by rows of `1`/`0` characters:
|
||||
|
||||
```
|
||||
cellSize 0.4
|
||||
agentRadius 0.45
|
||||
floorY 0.0
|
||||
...
|
||||
minX -5.0
|
||||
minZ -5.0
|
||||
gridWidth 50
|
||||
gridDepth 50
|
||||
11111111...
|
||||
10000001...
|
||||
```
|
||||
|
||||
This format is generated by `PathFinder::saveGrid()` after building from polygons and can be loaded much faster than recomputing from geometry.
|
||||
|
||||
### 2b. Polygon-based config (`.json` file)
|
||||
|
||||
The JSON file lists **navigation areas** (convex or concave walkable regions) and **obstacle polygons** (impassable zones within those regions):
|
||||
|
||||
```json
|
||||
{
|
||||
"cellSize": 0.4,
|
||||
"areas": [
|
||||
{ "name": "main_room", "available": true, "polygon": [[x,z], ...] }
|
||||
],
|
||||
"obstacles": [
|
||||
{ "name": "table", "polygon": [[x,z], ...] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Build steps:**
|
||||
|
||||
1. **Mark available areas walkable** — every cell whose center lies inside any `available` navigation area polygon gets `walkable = 1`. If `boundaryPadding > 0`, cells too close to the outer edge of the area are left blocked.
|
||||
2. **Mark obstacle polygons blocked** — cells whose center lies inside an obstacle polygon, or within `agentRadius + objectPadding` of its edges, are set to `0`.
|
||||
|
||||
Navigation areas can be toggled at runtime via `PathFinder::setAreaAvailable()`, which rebuilds the entire grid. This is used to open or close doors, gated areas, etc.
|
||||
|
||||
---
|
||||
|
||||
## 3. A\* Path Search
|
||||
|
||||
`PathFinder::findPath(start, end)` runs a standard A\* on the walkable grid.
|
||||
|
||||
**Neighbor connectivity:** 8-directional (cardinal + diagonal). Diagonal moves are blocked if either of the two adjacent cardinal cells is unwalkable (no corner-cutting).
|
||||
|
||||
**Step costs:** `1.0` for cardinal, `√2 ≈ 1.414` for diagonal.
|
||||
|
||||
**Heuristic:** Euclidean distance in cell units to the end cell.
|
||||
|
||||
**Start/end snapping:** If the exact cell for `start` or `end` is not walkable, `findNearestWalkableCell` expands a square ring outward (up to radius 8 m) to find the nearest walkable cell. This makes clicking slightly outside the nav mesh still produce a valid path.
|
||||
|
||||
**Path reconstruction:** After A\* completes, the cell chain is walked via `cameFrom[]` from `end` back to `start`, reversed, then smoothed (see §4).
|
||||
|
||||
**First-waypoint trimming:** If the first waypoint is within `cellSize × 0.75` of `start`, it is dropped (the character is already close enough).
|
||||
|
||||
**Last-waypoint precision:** If the requested `end` maps to the same cell as the snapped end cell, the last waypoint is replaced with the exact `end` world position rather than the cell centre.
|
||||
|
||||
---
|
||||
|
||||
## 4. Path Smoothing
|
||||
|
||||
Raw A\* paths follow the grid diagonals and produce staircase-shaped routes. A **string-pulling** (line-of-sight) pass compresses them:
|
||||
|
||||
```
|
||||
anchor = path[0]
|
||||
result = [anchor]
|
||||
while anchor is not the last cell:
|
||||
find the furthest cell 'next' from anchor with unobstructed line of sight
|
||||
result.append(next)
|
||||
anchor = next
|
||||
```
|
||||
|
||||
Line-of-sight is checked by stepping along the segment in increments of `cellSize / 2` and verifying that each sampled cell is walkable. The result is a minimal set of waypoints connected by straight, obstacle-free segments.
|
||||
|
||||
---
|
||||
|
||||
## 5. Approaching Unreachable Destinations
|
||||
|
||||
When a player clicks on a point in a disconnected region (e.g., across a thin wall), the original `findPath` returns an empty path and the character does not move. This is surprising — a click on a solid wall sensibly moves the character to the nearest reachable point, but a click into an inaccessible room does nothing.
|
||||
|
||||
**`findPathToNearest`** fixes this with a three-step cascade:
|
||||
|
||||
1. Try `findPath` with dynamic obstacles (stationary characters are avoided).
|
||||
2. If empty, retry `findPath` without dynamic obstacles (an NPC blocking a doorway is ignored).
|
||||
3. If still empty (destination genuinely unreachable), run **nearest-reachable A\***.
|
||||
|
||||
**Nearest-reachable A\***, implemented in `findNearestReachableImpl`:
|
||||
|
||||
- Runs the identical A\* loop against the static walkable grid.
|
||||
- While processing cells, tracks `bestIndex` — the already-visited cell with the smallest Euclidean distance (in cell units) to the end cell.
|
||||
- If A\* exhausts all reachable space without finding `end`, it reconstructs and returns a path to `bestIndex`.
|
||||
- If `bestIndex` is still the start cell (character is completely isolated), an empty path is returned and the character stays put.
|
||||
|
||||
The net effect: clicking anywhere in the world always moves the character as close as possible to the target, matching the behaviour of clicking on a solid wall.
|
||||
|
||||
`findPathToNearest` replaces the direct `findPath` call in `Location::setupNavigation`'s path planner lambda, so it applies equally to the player and all NPCs.
|
||||
|
||||
---
|
||||
|
||||
## 6. Dynamic Obstacles
|
||||
|
||||
When a path is planned, other characters can temporarily mark cells as blocked to make the character walk around them rather than through them.
|
||||
|
||||
**How it works:**
|
||||
|
||||
In `Location::setupNavigation`, every character is given a `PathPlanner` closure. Before calling `findPath`, the closure builds a list of `PathFinder::DynamicObstacle` entries (position + radius) representing nearby characters. `findPath` copies the static walkable grid, stamps zeros in circles around each obstacle, then runs A\* on the modified copy. The static grid is never mutated.
|
||||
|
||||
**Which characters become obstacles:**
|
||||
|
||||
A character is added as a dynamic obstacle only when **all** of these are true:
|
||||
|
||||
- It is not the character currently planning the path (`self`).
|
||||
- It is alive and enabled.
|
||||
- **It is not moving** — a moving character is transparent to pathfinding, so it does not block narrow corridors that it is actively passing through.
|
||||
- Its position lies within `kDynamicObstacleInfluenceDist = 6 m` of the direct line segment from `start` to `end` (distant characters do not affect the search).
|
||||
|
||||
**Obstacle radius:** `character.collisionRadius × 0.6`. Using 60 % of the physical collision radius makes path planning less conservative; physical separation at full radius is still enforced by collision resolution (§8).
|
||||
|
||||
**Fallback when dynamic obstacles block the only path:**
|
||||
|
||||
If step 1 of `findPathToNearest` (with dynamic obstacles) returns empty, step 2 retries without any dynamic obstacles. This handles the common case of an NPC standing in a doorway: the player paths through the NPC's position, and the nudge logic (§8) pushes the NPC aside as the player passes.
|
||||
|
||||
---
|
||||
|
||||
## 7. Path Following
|
||||
|
||||
`Character::setTarget(destination, onArrived)` sets a new walk target. It calls the path planner to generate a waypoint list. The result is stored in `pathWaypoints`; the final destination is also stored in `walkTarget` and `requestedWalkTarget`.
|
||||
|
||||
Each frame in `Character::update`:
|
||||
|
||||
1. **Active target** — if `pathWaypoints` is non-empty, the character moves toward `pathWaypoints[currentWaypointIndex]`; otherwise it moves toward `walkTarget`.
|
||||
2. **Movement** — the character advances along the XZ direction at `walkSpeed` m/s and rotates smoothly toward the movement direction at `rotationSpeed` rad/s.
|
||||
3. **Waypoint advance** — when the character is within `WALK_THRESHOLD = 0.05 m` of the current waypoint, it advances to the next one. When the last waypoint is reached, `pathWaypoints` is cleared and the optional `onArrived` callback is fired.
|
||||
4. **State machine** — the animation state switches between `STAND` and `WALK` based on whether the character is moving.
|
||||
|
||||
`Character::isMoving()` returns `true` if `pathWaypoints` is non-empty or the distance to `walkTarget` exceeds `WALK_THRESHOLD`. This is used by dynamic obstacle filtering and collision nudging.
|
||||
|
||||
**Stopping in place:** `Character::stopInPlace()` sets `walkTarget` and `requestedWalkTarget` to the current position and clears `pathWaypoints`. It is called when an external force (collision resolution) displaces a stationary player so that the player does not walk back to their previous target position.
|
||||
|
||||
---
|
||||
|
||||
## 8. Character Collision Resolution
|
||||
|
||||
Pathfinding alone does not prevent two characters from occupying the same space — it only steers paths around stationary characters. Physical separation is handled separately each frame by `Location::resolveCharacterCollisions`.
|
||||
|
||||
**Algorithm** (3 iterations per frame):
|
||||
|
||||
For every pair `(A, B)` of living, enabled characters:
|
||||
|
||||
1. Compute the overlap: `penetration = (collisionRadius_A + collisionRadius_B) - distance(A, B)`.
|
||||
2. If `penetration > 0`, compute a push direction (A-to-B normal) and a push magnitude of `penetration / 2` per character.
|
||||
3. Compute candidate new positions `newA` and `newB`.
|
||||
4. Validate against the navigation grid (`PathFinder::isWalkable`). If a pushed position is unwalkable, only the other character is moved.
|
||||
5. **Player stays put:** if the player was not moving (`!isMoving()`) before the push, `stopInPlace()` is called after the push so the player does not walk back to the old target.
|
||||
6. **NPC yielding:** if one character was moving and the other was standing, `nudgeCharacterAside` is called on the standing character.
|
||||
|
||||
**`nudgeCharacterAside(standing, awayFrom)`:**
|
||||
|
||||
Gives the standing NPC a short walk target so it steps out of the way:
|
||||
|
||||
1. Compute the direction from `awayFrom` to the NPC's current position.
|
||||
2. Try four candidate targets at distance `1.2 m` in directions: straight away, +90°, −90°, 180°.
|
||||
3. Use the first candidate that is walkable (per `PathFinder::isWalkable`).
|
||||
4. Call `standing->setTarget(candidate)` — the NPC takes a small step aside, then stands at the new spot.
|
||||
5. The player is never nudged; combat NPCs can be nudged, but their attack AI immediately overrides the yield target on the next tick.
|
||||
|
||||
---
|
||||
|
||||
## 9. Dynamic Replanning
|
||||
|
||||
When characters move they can displace each other or enter each other's planned paths. `Location::updateDynamicReplans` handles this:
|
||||
|
||||
**Every frame:**
|
||||
|
||||
1. Measure how much each character moved since the last frame. Characters that moved more than `kMovedEps = 0.05 m` are collected as **movers**.
|
||||
2. For each mover, find other characters that are currently walking. If the mover's position is within `kReplanTriggerDist = 1.8 m` of the segment `[walker.position → walker.nextWaypoint]`, trigger a replan for the walker via `forceReplan()`.
|
||||
3. A per-character cooldown of `kReplanCooldownMs = 500 ms` prevents the same character from replanning more often than twice per second.
|
||||
|
||||
**`Character::forceReplan()`** re-runs the path planner from the character's current position to its stored `requestedWalkTarget`, updating `pathWaypoints` in place. If the replanned path is empty, the character stops at its current position.
|
||||
|
||||
The relatively generous trigger distance (1.8 m vs the old 1.1 m) and cooldown (500 ms vs 300 ms) prevent micro-jitter: small position corrections from collision resolution no longer spam replanning events.
|
||||
|
||||
---
|
||||
|
||||
## 10. Key Constants Reference
|
||||
|
||||
| Constant | Location | Value | Description |
|
||||
|---|---|---|---|
|
||||
| `cellSize` | `PathFinder` config | 0.4 m | Grid cell size |
|
||||
| `agentRadius` | `PathFinder` config | 0.45 m | Character half-width for grid erosion |
|
||||
| `objectPadding` | `PathFinder` config | 0.25 m | Extra clearance around obstacles |
|
||||
| `WALK_THRESHOLD` | `Character.h` | 0.05 m | Distance below which a waypoint is considered reached |
|
||||
| `TARGET_REPLAN_THRESHOLD` | `Character.h` | 0.25 m | Deduplication threshold in `setTarget` |
|
||||
| `kDynamicObstacleInfluenceDist` | `Location.cpp` | 6.0 m | Max distance from path for a character to become an obstacle |
|
||||
| `kDynamicObstacleRadiusFraction` | `Location.cpp` | 0.6× | Fraction of collision radius used for dynamic obstacle footprint |
|
||||
| `kNudgeDist` | `Location.cpp` | 1.2 m | Distance an NPC steps aside when yielding |
|
||||
| `kReplanTriggerDist` | `Location.cpp` | 1.8 m | Mover must be this close to a walker's path to trigger replan |
|
||||
| `kReplanCooldownMs` | `Location.cpp` | 500 ms | Minimum interval between replans for any one character |
|
||||
| `NPC_TALK_DISTANCE` | `Location.cpp` | 1.35 m | Distance at which walking-to-NPC interaction fires |
|
||||
| `kIterations` (collision) | `Location.cpp` | 3 | Push-apart iterations per frame |
|
||||
@@ -1,186 +0,0 @@
|
||||
{
|
||||
"objects": [
|
||||
{
|
||||
"name": "Plane",
|
||||
"texturePath": "resources/w/exterior/Segmented_Plane002.png",
|
||||
"meshPath": "resources/w/exterior/Segmented_Plane002.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "door",
|
||||
"texturePath": "resources/w/exterior/door002.png",
|
||||
"meshPath": "resources/w/exterior/ext_door001.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "inai",
|
||||
"texturePath": "resources/w/exterior/Building_work014.png",
|
||||
"meshPath": "resources/w/exterior/int_building002.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "stairs",
|
||||
"texturePath": "resources/w/exterior/Staircase001.png",
|
||||
"meshPath": "resources/w/exterior/int_stairs001.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree001",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 10.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 12.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree002",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 19.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree003",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 8.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree004",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree005",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": -5.0,
|
||||
"positionZ": -8.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree006",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 8.49915,
|
||||
"positionY": -5.0,
|
||||
"positionZ": -2.59884,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree007",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 14.5936,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 5.3401,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree008",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 23.9295,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 9.00583,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree009",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 29.8128,
|
||||
"positionY": -5.0,
|
||||
"positionZ": -1.45278,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree010",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 33.1771,
|
||||
"positionY": -5.0,
|
||||
"positionZ": 14.609,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
{
|
||||
"objects": [
|
||||
{
|
||||
"name": "Plane",
|
||||
"texturePath": "resources/w/exterior/Segmented_Plane002.png",
|
||||
"textureDarkandsPath": "resources/w/exterior/darklands_Segmented_Plane002.png",
|
||||
"meshPath": "resources/w/exterior/Segmented_Plane002.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "door",
|
||||
"texturePath": "resources/w/exterior/door002.png",
|
||||
"textureDarkandsPath": "resources/w/exterior/darklands_door002.png",
|
||||
"meshPath": "resources/w/exterior/ext_door001.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "inai",
|
||||
"texturePath": "resources/w/exterior/Ext_Building004.png",
|
||||
"textureDarkandsPath": "resources/w/exterior/darklands_Ext_Building004.png",
|
||||
"meshPath": "resources/w/exterior/ext_building001.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 0.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree001",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 10.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 12.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree002",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 19.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree003",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 8.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree004",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 0.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree005",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -8.0,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree006",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 8.49915,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -2.59884,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree007",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 14.5936,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 5.3401,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree008",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 23.9295,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 9.00583,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree009",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 29.8128,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -1.45278,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
},
|
||||
{
|
||||
"name": "tree010",
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 33.1771,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 14.609,
|
||||
"scale": 1.0,
|
||||
"interactive": false
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 10.0,
|
||||
"positionY": 0.0,
|
||||
@@ -60,7 +60,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationY": 90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12,
|
||||
"positionY": 0.0,
|
||||
@@ -86,7 +86,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": 0.0,
|
||||
@@ -99,7 +99,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationY": 90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": 0.0,
|
||||
@@ -125,7 +125,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 14.5936,
|
||||
"positionY": 0.0,
|
||||
@@ -138,7 +138,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationY": 90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 23.9295,
|
||||
"positionY": 0.0,
|
||||
@@ -164,7 +164,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 33.1771,
|
||||
"positionY": 0.0,
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
"texturePath": "resources/w/interior/doors_tex001.png",
|
||||
"meshPath": "resources/w/interior/outputRoom_N_0_Leaf001.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5708,
|
||||
"rotationY": 90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 1.5,
|
||||
"positionY": 0.975,
|
||||
@@ -117,7 +117,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 10.0,
|
||||
"positionY": -5.0,
|
||||
@@ -129,7 +129,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationY": 90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12,
|
||||
"positionY": -5.0,
|
||||
@@ -153,7 +153,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": -5.0,
|
||||
@@ -165,7 +165,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationY": 90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": -12.0,
|
||||
"positionY": -5.0,
|
||||
@@ -189,7 +189,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 14.5936,
|
||||
"positionY": -5.0,
|
||||
@@ -201,7 +201,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 1.5707963267948966,
|
||||
"rotationY": 90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 23.9295,
|
||||
"positionY": -5.0,
|
||||
@@ -225,7 +225,7 @@
|
||||
"texturePath": "resources/w/exterior/tree001.png",
|
||||
"meshPath": "resources/w/exterior/tree003.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": -1.5707963267948966,
|
||||
"rotationY": -90,
|
||||
"rotationZ": 0.0,
|
||||
"positionX": 33.1771,
|
||||
"positionY": -5.0,
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
},
|
||||
{
|
||||
"name": "Computer001",
|
||||
"texturePath": "resources/w/interior/computer_texture001.png",
|
||||
"meshPath": "resources/w/interior/computer001_003.txt",
|
||||
"texturePath": "resources/w/interior/computer_texture002.png",
|
||||
"meshPath": "resources/w/interior/computer001_004.txt",
|
||||
"rotationX": 0.0,
|
||||
"rotationY": 0.0,
|
||||
"rotationZ": 0.0,
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"npcs": [
|
||||
{
|
||||
"id": "npc_04_ghost",
|
||||
"name": "Беспокойный Призрак",
|
||||
"texturePath": "resources/w/ghost_skin001.png",
|
||||
"animationIdlePath": "resources/w/default_float001.anim",
|
||||
"animationWalkPath": "resources/w/default_float001.anim",
|
||||
"positionX": -0.28594,
|
||||
"positionY": 0,
|
||||
"positionZ": 13.9641,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 1.0,
|
||||
"gift": {
|
||||
"id": "ghost_essence",
|
||||
"name": "Ghost's Essence",
|
||||
"description": "A mysterious essence from the Ghost realm",
|
||||
"icon": "resources/w/red.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ghost_01x",
|
||||
"name": "Опасный Призрак",
|
||||
"texturePath": "resources/w/ghost_skin002.png",
|
||||
"animationIdlePath": "resources/w/default_float001.anim",
|
||||
"animationWalkPath": "resources/w/default_float001.anim",
|
||||
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
|
||||
"animationActionAttackPath": "resources/w/float_attack003.anim",
|
||||
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
|
||||
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
|
||||
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
|
||||
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
|
||||
"positionX": 4.09561,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 6.28458,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"hp": 35,
|
||||
"canAttack": true
|
||||
},
|
||||
{
|
||||
"id": "ghost_02x",
|
||||
"name": "Злой призрак",
|
||||
"texturePath": "resources/w/ghost_skin002.png",
|
||||
"animationIdlePath": "resources/w/default_float001.anim",
|
||||
"animationWalkPath": "resources/w/default_float001.anim",
|
||||
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
|
||||
"animationActionAttackPath": "resources/w/float_attack003.anim",
|
||||
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
|
||||
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
|
||||
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
|
||||
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
|
||||
"positionX": -4.95651,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 5.81422,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"hp": 25,
|
||||
"canAttack": true
|
||||
},
|
||||
{
|
||||
"id": "ghost_02x",
|
||||
"name": "Злой дух",
|
||||
"texturePath": "resources/w/ghost_skin002.png",
|
||||
"animationIdlePath": "resources/w/default_float001.anim",
|
||||
"animationWalkPath": "resources/w/default_float001.anim",
|
||||
"animationActionIdlePath": "resources/w/float_attack003_cut.anim",
|
||||
"animationActionAttackPath": "resources/w/float_attack003.anim",
|
||||
"animationStandToActionPath": "resources/w/default_float001_cut.anim",
|
||||
"animationActionToStandPath": "resources/w/default_float001_cut.anim",
|
||||
"animationActionToDeathPath": "resources/w/default_float001_cut.anim",
|
||||
"animationDeathIdlePath": "resources/w/default_float001_cut.anim",
|
||||
"positionX": 0,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 10,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"hp": 15,
|
||||
"canAttack": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
{
|
||||
"npcs": [
|
||||
{
|
||||
"id": "npc_01_default",
|
||||
"name": "Студент",
|
||||
"animationIdlePath": "resources/w/jam/man_stand_idle002.anim",
|
||||
"animationWalkPath": "resources/w/jam/man_walk002.anim",
|
||||
"meshTextures": {
|
||||
"Body": "resources/w/jam/male_packed0_diffuse.png",
|
||||
"Bottoms": "resources/w/jam/male_packed1_diffuse.png",
|
||||
"Eyelashes": "resources/w/jam/male_packed0_diffuse.png",
|
||||
"Eyes": "resources/w/jam/male_packed0_diffuse.png",
|
||||
"Eyewear": "resources/w/jam/male_packed0_diffuse.png",
|
||||
"Gloves": "resources/w/jam/male_packed1_diffuse.png",
|
||||
"Hair": "resources/w/jam/male_packed0_diffuse.png",
|
||||
"Shoes": "resources/w/jam/male_packed1_diffuse.png",
|
||||
"Tops": "resources/w/jam/male_packed2_diffuse.png"
|
||||
},
|
||||
"positionX": 2.95,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 16.65,
|
||||
"facingAngle" : 3.141592,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.0001,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0,
|
||||
"gift": {
|
||||
"id": "guard_token",
|
||||
"name": "Guard's Token",
|
||||
"description": "A token from the Guard - sign of respect",
|
||||
"icon": "resources/w/red.png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "npc_02_woman",
|
||||
"name": "Студентка",
|
||||
"animationIdlePath": "resources/w/girl/girl_walk001.txt",
|
||||
"animationWalkPath": "resources/w/girl/girl_walk001.txt",
|
||||
"meshTextures": {
|
||||
"polySurface1": "resources/w/girl/Chat_02_diff_1.png"
|
||||
},
|
||||
"positionX": 19.5,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 32.0,
|
||||
"facingAngle" : 3.141592,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.01,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0
|
||||
},
|
||||
{
|
||||
"id": "npc_03_salesman",
|
||||
"name": "Мухтар Байке",
|
||||
"animationIdlePath": "resources/w/jam/salesperson_stand_idle003.anim",
|
||||
"animationWalkPath": "resources/w/jam/salesperson_walk001.anim",
|
||||
"meshTextures": {
|
||||
"Body": "resources/w/jam/Salesperson_packed0_diffuse.png",
|
||||
"Bottoms": "resources/w/jam/Salesperson_packed2_diffuse.png",
|
||||
"Eyelashes": "resources/w/jam/Salesperson_packed0_diffuse.png",
|
||||
"Eyes": "resources/w/jam/Salesperson_packed0_diffuse.png",
|
||||
"Hats": "resources/w/jam/Salesperson_packed1_diffuse.png",
|
||||
"Mustashes": "resources/w/jam/Salesperson_packed1_diffuse.png",
|
||||
"Shoes": "resources/w/jam/Salesperson_packed1_diffuse.png",
|
||||
"Tops": "resources/w/jam/Salesperson_packed1_diffuse.png"
|
||||
},
|
||||
"positionX": -1.94774,
|
||||
"positionY": 0.0,
|
||||
"positionZ": 16.2712,
|
||||
"facingAngle" : 3.141592,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.001,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0
|
||||
},
|
||||
{
|
||||
"id": "npc_teacher",
|
||||
"name": "Аида Токтоналы",
|
||||
"animationIdlePath": "resources/w/new_anims/teacher_stand_idle001.txt",
|
||||
"animationWalkPath": "resources/w/new_anims/woman_walk4_001.txt",
|
||||
"meshTextures": {
|
||||
"Teacher_lowpoly": "resources/w/new_anims/UniV_Grid_2K.001_Base_color.png"
|
||||
},
|
||||
"positionX": -3.76765,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -21.8495,
|
||||
"facingAngle" : 0,
|
||||
"walkSpeed": 1.3,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 1.0,
|
||||
"modelCorrectionRotX": -90.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0
|
||||
},
|
||||
{
|
||||
"id": "npc_teacher02",
|
||||
"name": "Асель Дженибековна",
|
||||
"animationIdlePath": "resources/w/new_anims/teacher_stand_idle001.txt",
|
||||
"animationWalkPath": "resources/w/new_anims/woman_walk4_001.txt",
|
||||
"meshTextures": {
|
||||
"Teacher_lowpoly": "resources/w/new_anims/UniV_Grid_2K.001_Base_color.png"
|
||||
},
|
||||
"positionX": 12.7499,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -20.8708,
|
||||
"facingAngle" : 0,
|
||||
"walkSpeed": 1.3,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 1.0,
|
||||
"modelCorrectionRotX": -90.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,19 +3,19 @@
|
||||
{
|
||||
"id": "npc_01_woman",
|
||||
"name": "Бермет",
|
||||
"animationIdlePath": "resources/w/girl/girl_idle001.txt",
|
||||
"animationWalkPath": "resources/w/girl/girl_walk010.txt",
|
||||
"animationIdlePath": "resources/w/girlfriend/girlfriend_idle003.txt",
|
||||
"animationWalkPath": "resources/w/girlfriend/girlfriend_walk003.txt",
|
||||
"meshTextures": {
|
||||
"polySurface1": "resources/w/girl/Chat_02_diff_1r006.png"
|
||||
"Girl_Low": "resources/w/girlfriend/Girl_Base_color.png"
|
||||
},
|
||||
"positionX": 1.03298,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -4.61801,
|
||||
"facingAngle" : 3.141592,
|
||||
"facingAngle" : 180,
|
||||
"walkSpeed": 1.8,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.016,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelScale": 1.0,
|
||||
"modelCorrectionRotX": -90.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0
|
||||
@@ -59,7 +59,7 @@
|
||||
"positionX": -6.33478,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -15.0382,
|
||||
"facingAngle" : 3.141592,
|
||||
"facingAngle" : 180,
|
||||
"walkSpeed": 1.5,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.0001,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"walkSpeed": 1.3,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 1.0,
|
||||
"facingAngle" : 3.141592,
|
||||
"facingAngle" : 180,
|
||||
"modelCorrectionRotX": -90.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
@@ -23,19 +23,19 @@
|
||||
{
|
||||
"id": "npc_02_woman",
|
||||
"name": "Айпери",
|
||||
"animationIdlePath": "resources/w/girl/girl_idle001.txt",
|
||||
"animationWalkPath": "resources/w/girl/girl_walk002.txt",
|
||||
"animationIdlePath": "resources/w/girlfriend/girlfriend_idle003.txt",
|
||||
"animationWalkPath": "resources/w/girlfriend/girlfriend_walk003.txt",
|
||||
"meshTextures": {
|
||||
"polySurface1": "resources/w/girl/Chat_02_diff_1r006.png"
|
||||
"Girl_Low": "resources/w/girlfriend/Girl_Base_color.png"
|
||||
},
|
||||
"positionX": 0.764049,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -8.53475,
|
||||
"facingAngle" : 3.141592,
|
||||
"facingAngle" : 180,
|
||||
"walkSpeed": 1.66,
|
||||
"rotationSpeed": 8.0,
|
||||
"modelScale": 0.016,
|
||||
"modelCorrectionRotX": 0.0,
|
||||
"modelScale": 1.0,
|
||||
"modelCorrectionRotX": -90.0,
|
||||
"modelCorrectionRotY": 180.0,
|
||||
"modelCorrectionRotZ": 0.0,
|
||||
"interactionRadius": 2.0,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"teleports": [
|
||||
{
|
||||
"id": "tp_loc2_to_loc1",
|
||||
"positionX": 8.2,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -9.9,
|
||||
"radius": 1.5,
|
||||
"active": true,
|
||||
"destinationLocation": "uni_interior",
|
||||
"destinationPositionX": 2.64621,
|
||||
"destinationPositionY": 0.0,
|
||||
"destinationPositionZ": -9.37259,
|
||||
"destinationRotationY": -1.5708
|
||||
},
|
||||
{
|
||||
"id": "tp_loc2_to_dorm",
|
||||
"positionX": -21.7327,
|
||||
"positionY": 0.0,
|
||||
"positionZ": -34.1036,
|
||||
"radius": 2.5,
|
||||
"active": true,
|
||||
"destinationLocation": "location_dorm",
|
||||
"destinationPositionX": -8.32343,
|
||||
"destinationPositionY": 0.0,
|
||||
"destinationPositionZ": -0.152264,
|
||||
"destinationRotationY": 1.5708
|
||||
}
|
||||
]
|
||||
}
|
||||
File renamed without changes.
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"trigger_zones": [
|
||||
]
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
"positionZ": 6.17516,
|
||||
"radius": 2.0,
|
||||
"hysteresis": 0.1,
|
||||
"enabled": false
|
||||
"enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -833,6 +833,132 @@
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "book_dialog003",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Да, вот эта книга! Я возьму ее.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "book_dialog004",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Я вернул книгу на место.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "book_dialog005",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "На этой полке лежат самые скучные книги в этом кабинете.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "book_dialog006",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Я надеюсь, мне эта книга больше не пригодится.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "computer_dialog001",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Это старый библиотечный компьютер, он даже не подключен к интернету.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "computer_dialog002",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "От этого компьютера сейчас не будет никакого толку.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "computer_dialog003",
|
||||
"start": "line_1",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "line_1",
|
||||
"type": "Line",
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/w/gg/gg2_s_podsvetkoy5.png",
|
||||
"text": "Надеюсь мне больше не придется притрагиваться к этому компьютеру.",
|
||||
"next": "end_1"
|
||||
},
|
||||
{
|
||||
"id": "end_1",
|
||||
"type": "End"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "dialog_report_card001",
|
||||
@@ -936,7 +1062,9 @@
|
||||
},
|
||||
{
|
||||
"id": "test_cutscene_02",
|
||||
"background": "resources/test_cutscene001.png",
|
||||
"background": "resources/black.png",
|
||||
"backgroundWidth" : 1280,
|
||||
"backgroundHeight" : 720,
|
||||
"durationMs": 5000,
|
||||
"fadeOutMs": 500,
|
||||
"fadeInMs": 500,
|
||||
@@ -980,7 +1108,9 @@
|
||||
},
|
||||
{
|
||||
"id": "darklands_exit001",
|
||||
"background": "resources/test_cutscene001.png",
|
||||
"background": "resources/black.png",
|
||||
"backgroundWidth" : 1280,
|
||||
"backgroundHeight" : 720,
|
||||
"durationMs": 5000,
|
||||
"fadeOutMs": 500,
|
||||
"fadeInMs": 500,
|
||||
@@ -1002,21 +1132,23 @@
|
||||
],
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/hero.png",
|
||||
"text": "Кажется, после того как я умер, я очнулся.",
|
||||
"text": "Мгновенно как я упал без сил, что-то сверкнуло.",
|
||||
"durationMs": 3000
|
||||
},
|
||||
{
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/hero.png",
|
||||
"text": "Жесть вообще, хочу спать.",
|
||||
"text": "Я открыл глаза и понял, что я по-прежнему в универе.",
|
||||
"durationMs": 3000
|
||||
},
|
||||
{
|
||||
"speaker": "Бекзат",
|
||||
"portrait": "resources/hero.png",
|
||||
"text": "Который час?",
|
||||
"text": "Все тело болело, как будто я всю ночь таскал мешки с цементом.",
|
||||
"durationMs": 3000
|
||||
},
|
||||
{
|
||||
"portrait": "resources/hero.png",
|
||||
"text": "А еще мне сильно хотелось спать...",
|
||||
"durationMs": 2000,
|
||||
"background": "resources/test_cutscene001.png"
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -84,21 +84,35 @@ game_api.npc_walk_to(0, -4.57412, 0, 6.78495, on_teacher_arrived2)
|
||||
end
|
||||
|
||||
function on_book_pickup()
|
||||
print("on_book_pickup")
|
||||
local day = game_api.getIntValue("day")
|
||||
if (teacher_told_about_book) then
|
||||
print("on_book_pickup step1")
|
||||
if not player_hold_book then
|
||||
if (night_time) or (day >=1) then
|
||||
game_api.start_dialogue("book_dialog006")
|
||||
else
|
||||
game_api.pickup_item("book")
|
||||
game_api.deactivate_interactive_object("Book001")
|
||||
player_hold_book = true
|
||||
game_api.start_dialogue("book_dialog003")
|
||||
--game_api.set_trigger_zone_enabled(3, true)
|
||||
end
|
||||
else
|
||||
game_api.remove_item("book")
|
||||
game_api.activate_interactive_object("Book001")
|
||||
player_hold_book = false
|
||||
game_api.start_dialogue("book_dialog004")
|
||||
--game_api.set_trigger_zone_enabled(3, false)
|
||||
end
|
||||
else
|
||||
print("on_book_pickup step2")
|
||||
game_api.start_dialogue("book_dialog005")
|
||||
end
|
||||
end
|
||||
|
||||
function on_bookshelf_clicked()
|
||||
if not player_hold_book then
|
||||
--[[if not player_hold_book then
|
||||
game_api.pickup_item("book")
|
||||
game_api.deactivate_interactive_object("Book001")
|
||||
player_hold_book = true
|
||||
@@ -108,7 +122,8 @@ function on_bookshelf_clicked()
|
||||
game_api.activate_interactive_object("Book001")
|
||||
player_hold_book = false
|
||||
game_api.set_trigger_zone_enabled(3, false)
|
||||
end
|
||||
end]]
|
||||
on_book_pickup()
|
||||
end
|
||||
|
||||
|
||||
@@ -353,11 +368,16 @@ print("Teacher arrived2")
|
||||
end
|
||||
|
||||
function book_dialog_zone001_enter_callback()
|
||||
print("book_dialog_zone001_enter_callback step 1")
|
||||
if (game_api.is_darklands()) then
|
||||
print("book_dialog_zone001_enter_callback step 2")
|
||||
game_api.start_dialogue("ghost_dialog002")
|
||||
game_api.set_trigger_zone_enabled(3, false)
|
||||
else
|
||||
if (night_time == false) then
|
||||
|
||||
print("book_dialog_zone001_enter_callback step 3")
|
||||
if (player_hold_book) and (night_time == false) then
|
||||
print("book_dialog_zone001_enter_callback step 4")
|
||||
game_api.start_dialogue("book_dialog001")
|
||||
game_api.switch_navigation(5)
|
||||
end
|
||||
@@ -365,7 +385,8 @@ function book_dialog_zone001_enter_callback()
|
||||
end
|
||||
|
||||
function book_dialog_zone001_exit_callback()
|
||||
if (night_time == false) then
|
||||
print("book_dialog_zone001_exit_callback step 1")
|
||||
if (player_hold_book) and (night_time == false) then
|
||||
game_api.switch_navigation(3)
|
||||
end
|
||||
end
|
||||
@@ -376,14 +397,27 @@ print("on_computer_clicked--")
|
||||
|
||||
if (day == 1) then
|
||||
-- TODO: some dialog like I don't need it
|
||||
print("on_computer_clicked--1")
|
||||
game_api.start_dialogue("computer_dialog003")
|
||||
else
|
||||
|
||||
if teacher_told_about_book then
|
||||
print("on_computer_clicked--2")
|
||||
if (night_time == false) then
|
||||
print("on_computer_clicked--3")
|
||||
if (player_hold_book) then
|
||||
print("on_computer_clicked--4")
|
||||
game_api.start_cutscene("test_cutscene_02")
|
||||
else
|
||||
print("on_computer_clicked--5")
|
||||
game_api.start_dialogue("book_dialog002")
|
||||
end
|
||||
else
|
||||
print("on_computer_clicked--6")
|
||||
game_api.start_dialogue("computer_dialog002")
|
||||
end
|
||||
else
|
||||
print("on_computer_clicked--7")
|
||||
game_api.start_dialogue("computer_dialog001")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -469,6 +503,8 @@ game_api.set_darklands_callbacks(
|
||||
first_time_darklands = false
|
||||
end
|
||||
|
||||
game_api.set_trigger_zone_enabled(1, false)
|
||||
|
||||
game_api.set_npc_enabled(0, false)
|
||||
game_api.set_npc_enabled(1, false)
|
||||
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,182 +0,0 @@
|
||||
===Vertices (Split by UV/Normal): 119
|
||||
V 0: Pos(-0.321174, -0.380694, 0.03328) Norm(-0.577341, -0.577355, 0.577355) UV(0.622728, 0.985786)
|
||||
V 1: Pos(-0.321174, -0.080976, -0.12644) Norm(-0.577341, 0.577355, -0.577355) UV(0.701365, 0.838077)
|
||||
V 2: Pos(-0.321174, -0.380694, -0.12644) Norm(-0.57735, -0.577325, -0.577377) UV(0.701365, 0.985786)
|
||||
V 3: Pos(-0.321174, -0.080976, 0.03328) Norm(-0.57735, 0.577325, 0.577377) UV(0.84347, 0.785241)
|
||||
V 4: Pos(0.038545, -0.080976, -0.12644) Norm(0.57735, 0.577325, -0.577377) UV(0.764763, 0.962461)
|
||||
V 5: Pos(-0.321174, -0.080976, -0.12644) Norm(-0.577341, 0.577355, -0.577355) UV(0.764763, 0.785276)
|
||||
V 6: Pos(0.038545, -0.080976, 0.03328) Norm(0.577341, 0.577355, 0.577355) UV(-0.0, 0.985999)
|
||||
V 7: Pos(0.038545, -0.380694, -0.12644) Norm(0.577341, -0.577355, -0.577355) UV(0.078326, 0.83829)
|
||||
V 8: Pos(0.038545, -0.080976, -0.12644) Norm(0.57735, 0.577325, -0.577377) UV(0.078326, 0.985999)
|
||||
V 9: Pos(0.038545, -0.380694, 0.03328) Norm(0.57735, -0.577325, 0.577377) UV(0.843469, 0.962426)
|
||||
V 10: Pos(-0.321174, -0.380694, -0.12644) Norm(-0.57735, -0.577325, -0.577377) UV(0.922175, 0.785276)
|
||||
V 11: Pos(0.038545, -0.380694, -0.12644) Norm(0.577341, -0.577355, -0.577355) UV(0.922175, 0.962461)
|
||||
V 12: Pos(0.038545, -0.080976, -0.12644) Norm(0.57735, 0.577325, -0.577377) UV(0.717688, 0.0)
|
||||
V 13: Pos(-0.321174, -0.380694, -0.12644) Norm(-0.57735, -0.577325, -0.577377) UV(0.865397, 0.17727)
|
||||
V 14: Pos(-0.321174, -0.080976, -0.12644) Norm(-0.577341, 0.577355, -0.577355) UV(0.717688, 0.17727)
|
||||
V 15: Pos(-0.321174, -0.080976, 0.03328) Norm(-0.57735, 0.577325, 0.577377) UV(0.764763, 0.60813)
|
||||
V 16: Pos(0.038545, -0.380694, 0.03328) Norm(0.57735, -0.577325, 0.577377) UV(0.912472, 0.785241)
|
||||
V 17: Pos(0.038545, -0.080976, 0.03328) Norm(0.577341, 0.577355, 0.577355) UV(0.764763, 0.785241)
|
||||
V 18: Pos(-0.614892, 0.342792, 0.284424) Norm(-0.57734, -0.577355, 0.577355) UV(0.865397, 0.0)
|
||||
V 19: Pos(-0.614892, 0.561483, -0.134268) Norm(-0.57734, 0.577355, -0.577355) UV(0.973174, 0.206141)
|
||||
V 20: Pos(-0.614892, 0.342792, -0.134268) Norm(-0.577355, -0.577371, -0.577325) UV(0.865397, 0.206141)
|
||||
V 21: Pos(-0.614892, 0.561483, 0.284424) Norm(-0.577355, 0.577371, 0.577325) UV(0.206323, 0.553153)
|
||||
V 22: Pos(-0.0362, 0.561483, -0.134268) Norm(0.577355, 0.577371, -0.577325) UV(0.0, 0.83829)
|
||||
V 23: Pos(-0.614892, 0.561483, -0.134268) Norm(-0.57734, 0.577355, -0.577355) UV(0.0, 0.553246)
|
||||
V 24: Pos(-0.0362, 0.561483, 0.284424) Norm(0.57734, 0.577355, 0.577355) UV(0.867943, 0.277359)
|
||||
V 25: Pos(-0.0362, 0.342792, -0.134268) Norm(0.57734, -0.577355, -0.577355) UV(0.97572, 0.482684)
|
||||
V 26: Pos(-0.0362, 0.561483, -0.134268) Norm(0.577355, 0.577371, -0.577325) UV(0.867943, 0.482684)
|
||||
V 27: Pos(-0.0362, 0.342792, 0.284424) Norm(0.577355, -0.577371, 0.577325) UV(0.206322, 0.838197)
|
||||
V 28: Pos(-0.614892, 0.342792, -0.134268) Norm(-0.577355, -0.577371, -0.577325) UV(0.412645, 0.553246)
|
||||
V 29: Pos(-0.0362, 0.342792, -0.134268) Norm(0.57734, -0.577355, -0.577355) UV(0.412645, 0.83829)
|
||||
V 30: Pos(-0.0362, 0.561483, -0.134268) Norm(0.577355, 0.577371, -0.577325) UV(0.514952, 0.553153)
|
||||
V 31: Pos(-0.614892, 0.342792, -0.134268) Norm(-0.577355, -0.577371, -0.577325) UV(0.622728, 0.838334)
|
||||
V 32: Pos(-0.614892, 0.561483, -0.134268) Norm(-0.57734, 0.577355, -0.577355) UV(0.514952, 0.838334)
|
||||
V 33: Pos(-0.614892, 0.561483, 0.284424) Norm(-0.577355, 0.577371, 0.577325) UV(0.622728, 0.553153)
|
||||
V 34: Pos(-0.0362, 0.342792, 0.284424) Norm(0.577355, -0.577371, 0.577325) UV(0.730505, 0.838077)
|
||||
V 35: Pos(-0.0362, 0.561483, 0.284424) Norm(0.57734, 0.577355, 0.577355) UV(0.622728, 0.838077)
|
||||
V 36: Pos(-0.04492, -0.422368, 0.425114) Norm(-0.964019, -0.126929, -0.233572) UV(0.551589, 0.17729)
|
||||
V 37: Pos(0.154366, -0.062626, -0.106573) Norm(0.092451, 0.354611, -0.930432) UV(0.717688, 0.0)
|
||||
V 38: Pos(0.154366, -0.422368, -0.106573) Norm(0.09503, -0.342725, -0.934617) UV(0.717688, 0.17729)
|
||||
V 39: Pos(-0.04492, -0.062626, 0.425114) Norm(-0.95805, 0.148128, -0.245352) UV(0.049252, 0.277359)
|
||||
V 40: Pos(0.16953, 0.037313, 0.020093) Norm(0.364582, 0.91558, -0.169683) UV(0.275794, -0.0)
|
||||
V 41: Pos(0.154366, -0.062626, -0.106573) Norm(0.092451, 0.354611, -0.930432) UV(0.226542, 0.277359)
|
||||
V 42: Pos(-0.049131, 0.037313, 0.535225) Norm(-0.186957, 0.70094, 0.68828) UV(0.27372, 0.553153)
|
||||
V 43: Pos(0.16953, -0.522306, 0.020093) Norm(0.391468, -0.900748, -0.188163) UV(0.537026, 0.277359)
|
||||
V 44: Pos(0.16953, 0.037313, 0.020093) Norm(0.364582, 0.91558, -0.169683) UV(0.537026, 0.553153)
|
||||
V 45: Pos(-0.049131, -0.522306, 0.535225) Norm(-0.192057, -0.680692, 0.706946) UV(0.551589, -0.0)
|
||||
V 46: Pos(0.154366, -0.422368, -0.106573) Norm(0.09503, -0.342725, -0.934617) UV(0.325047, 0.277359)
|
||||
V 47: Pos(0.16953, -0.522306, 0.020093) Norm(0.391468, -0.900748, -0.188163) UV(0.275794, -0.0)
|
||||
V 48: Pos(0.16953, 0.037313, 0.020093) Norm(0.364582, 0.91558, -0.169683) UV(-0.0, 0.553153)
|
||||
V 49: Pos(0.154366, -0.422368, -0.106573) Norm(0.09503, -0.342725, -0.934617) UV(0.27372, 0.326611)
|
||||
V 50: Pos(0.154366, -0.062626, -0.106573) Norm(0.092451, 0.354611, -0.930432) UV(0.27372, 0.503901)
|
||||
V 51: Pos(-0.04492, -0.062626, 0.425114) Norm(-0.95805, 0.148128, -0.245352) UV(0.537025, 0.503901)
|
||||
V 52: Pos(-0.049131, -0.522306, 0.535225) Norm(-0.192057, -0.680692, 0.706946) UV(0.764763, 0.277359)
|
||||
V 53: Pos(-0.049131, 0.037313, 0.535225) Norm(-0.186957, 0.70094, 0.68828) UV(0.764763, 0.553154)
|
||||
V 54: Pos(0.269496, -0.567769, -0.048135) Norm(-0.468336, -0.577333, 0.668841) UV(0.730505, 0.883925)
|
||||
V 55: Pos(0.26061, 0.103404, -0.098532) Norm(-0.66884, 0.577347, -0.468319) UV(0.755124, 0.553154)
|
||||
V 56: Pos(0.26061, -0.567769, -0.098532) Norm(-0.668841, -0.57736, -0.468302) UV(0.755124, 0.883925)
|
||||
V 57: Pos(0.269496, 0.103404, -0.048135) Norm(-0.468342, 0.577346, 0.668826) UV(0.893159, 0.482684)
|
||||
V 58: Pos(0.468575, 0.103404, -0.135202) Norm(0.468342, 0.577346, -0.668826) UV(0.867943, 0.586724)
|
||||
V 59: Pos(0.26061, 0.103404, -0.098532) Norm(-0.66884, 0.577347, -0.468319) UV(0.867943, 0.482698)
|
||||
V 60: Pos(0.477461, 0.103404, -0.084805) Norm(0.66884, 0.577347, 0.468319) UV(0.97572, 0.60813)
|
||||
V 61: Pos(0.468575, -0.567769, -0.135202) Norm(0.468336, -0.577333, -0.668841) UV(1.0, 0.277359)
|
||||
V 62: Pos(0.468575, 0.103404, -0.135202) Norm(0.468342, 0.577346, -0.668826) UV(1.0, 0.60813)
|
||||
V 63: Pos(0.468575, -0.567769, -0.135202) Norm(0.468336, -0.577333, -0.668841) UV(0.918374, 0.586724)
|
||||
V 64: Pos(0.269496, -0.567769, -0.048135) Norm(-0.468336, -0.577333, 0.668841) UV(0.893159, 0.482684)
|
||||
V 65: Pos(0.26061, -0.567769, -0.098532) Norm(-0.668841, -0.57736, -0.468302) UV(0.918374, 0.482698)
|
||||
V 66: Pos(0.468575, 0.103404, -0.135202) Norm(0.468342, 0.577346, -0.668826) UV(0.412645, 0.883924)
|
||||
V 67: Pos(0.26061, -0.567769, -0.098532) Norm(-0.668841, -0.57736, -0.468302) UV(0.514952, 0.553153)
|
||||
V 68: Pos(0.26061, 0.103404, -0.098532) Norm(-0.66884, 0.577347, -0.468319) UV(0.514952, 0.883924)
|
||||
V 69: Pos(0.269496, 0.103404, -0.048135) Norm(-0.468342, 0.577346, 0.668826) UV(0.764763, 0.60813)
|
||||
V 70: Pos(0.477461, -0.567769, -0.084805) Norm(0.668841, -0.57736, 0.468302) UV(0.867943, 0.277359)
|
||||
V 71: Pos(0.477461, 0.103404, -0.084805) Norm(0.66884, 0.577347, 0.468319) UV(0.867943, 0.60813)
|
||||
V 72: Pos(0.243142, 0.221328, -0.083115) Norm(-0.577358, -0.577347, 0.577347) UV(0.551589, 0.260175)
|
||||
V 73: Pos(0.243142, 0.285422, -0.127209) Norm(-0.577358, 0.577347, -0.577347) UV(0.573298, 0.228587)
|
||||
V 74: Pos(0.243142, 0.221328, -0.127209) Norm(-0.577359, -0.577332, -0.577359) UV(0.573298, 0.260175)
|
||||
V 75: Pos(0.243142, 0.285422, -0.083115) Norm(-0.577359, 0.577332, 0.577359) UV(0.658221, 0.17729)
|
||||
V 76: Pos(0.347237, 0.285422, -0.127209) Norm(0.577359, 0.577332, -0.577359) UV(0.636492, 0.228573)
|
||||
V 77: Pos(0.243142, 0.285422, -0.127209) Norm(-0.577358, 0.577347, -0.577347) UV(0.636492, 0.177299)
|
||||
V 78: Pos(0.347237, 0.285422, -0.083115) Norm(0.577358, 0.577347, 0.577347) UV(0.573298, 0.260175)
|
||||
V 79: Pos(0.347237, 0.221328, -0.127209) Norm(0.577358, -0.577347, -0.577347) UV(0.594921, 0.228588)
|
||||
V 80: Pos(0.347237, 0.285422, -0.127209) Norm(0.577359, 0.577332, -0.577359) UV(0.594921, 0.260175)
|
||||
V 81: Pos(0.347237, 0.221328, -0.083115) Norm(0.577359, -0.577332, 0.577359) UV(0.614763, 0.228563)
|
||||
V 82: Pos(0.243142, 0.221328, -0.127209) Norm(-0.577359, -0.577332, -0.577359) UV(0.636492, 0.177299)
|
||||
V 83: Pos(0.347237, 0.221328, -0.127209) Norm(0.577358, -0.577347, -0.577347) UV(0.636492, 0.228573)
|
||||
V 84: Pos(0.347237, 0.285422, -0.127209) Norm(0.577359, 0.577332, -0.577359) UV(0.551589, 0.17729)
|
||||
V 85: Pos(0.243142, 0.221328, -0.127209) Norm(-0.577359, -0.577332, -0.577359) UV(0.583176, 0.228588)
|
||||
V 86: Pos(0.243142, 0.285422, -0.127209) Norm(-0.577358, 0.577347, -0.577347) UV(0.551589, 0.228588)
|
||||
V 87: Pos(0.243142, 0.285422, -0.083115) Norm(-0.577359, 0.577332, 0.577359) UV(0.583176, 0.17729)
|
||||
V 88: Pos(0.347237, 0.221328, -0.083115) Norm(0.577359, -0.577332, 0.577359) UV(0.614763, 0.228541)
|
||||
V 89: Pos(0.347237, 0.285422, -0.083115) Norm(0.577358, 0.577347, 0.577347) UV(0.583176, 0.228541)
|
||||
V 90: Pos(-0.321174, -0.080976, 0.03328) Norm(-0.57735, 0.577325, 0.577377) UV(0.622728, 0.838077)
|
||||
V 91: Pos(0.038545, -0.080976, 0.03328) Norm(0.577341, 0.577355, 0.577355) UV(0.84347, 0.962426)
|
||||
V 92: Pos(0.038545, -0.380694, 0.03328) Norm(0.57735, -0.577325, 0.577377) UV(-0.0, 0.83829)
|
||||
V 93: Pos(-0.321174, -0.380694, 0.03328) Norm(-0.577341, -0.577355, 0.577355) UV(0.843469, 0.785241)
|
||||
V 94: Pos(0.038545, -0.380694, -0.12644) Norm(0.577341, -0.577355, -0.577355) UV(0.865397, 0.0)
|
||||
V 95: Pos(-0.321174, -0.380694, 0.03328) Norm(-0.577341, -0.577355, 0.577355) UV(0.912472, 0.60813)
|
||||
V 96: Pos(-0.614892, 0.561483, 0.284424) Norm(-0.577355, 0.577371, 0.577325) UV(0.973174, 0.0)
|
||||
V 97: Pos(-0.0362, 0.561483, 0.284424) Norm(0.57734, 0.577355, 0.577355) UV(0.206323, 0.838198)
|
||||
V 98: Pos(-0.0362, 0.342792, 0.284424) Norm(0.577355, -0.577371, 0.577325) UV(0.97572, 0.277359)
|
||||
V 99: Pos(-0.614892, 0.342792, 0.284424) Norm(-0.57734, -0.577355, 0.577355) UV(0.206322, 0.553153)
|
||||
V 100: Pos(-0.0362, 0.342792, -0.134268) Norm(0.57734, -0.577355, -0.577355) UV(0.622728, 0.553153)
|
||||
V 101: Pos(-0.614892, 0.342792, 0.284424) Norm(-0.57734, -0.577355, 0.577355) UV(0.730505, 0.553153)
|
||||
V 102: Pos(-0.04492, -0.062626, 0.425114) Norm(-0.95805, 0.148128, -0.245352) UV(0.551589, 0.0)
|
||||
V 103: Pos(-0.049131, 0.037313, 0.535225) Norm(-0.186957, 0.70094, 0.68828) UV(-0.0, -0.0)
|
||||
V 104: Pos(-0.049131, -0.522306, 0.535225) Norm(-0.192057, -0.680692, 0.706946) UV(0.27372, 0.277359)
|
||||
V 105: Pos(-0.04492, -0.422368, 0.425114) Norm(-0.964019, -0.126929, -0.233572) UV(0.502336, 0.277359)
|
||||
V 106: Pos(0.16953, -0.522306, 0.020093) Norm(0.391468, -0.900748, -0.188163) UV(-0.0, 0.277359)
|
||||
V 107: Pos(-0.04492, -0.422368, 0.425114) Norm(-0.964019, -0.126929, -0.233572) UV(0.537025, 0.326611)
|
||||
V 108: Pos(0.269496, 0.103404, -0.048135) Norm(-0.468342, 0.577346, 0.668826) UV(0.730505, 0.553154)
|
||||
V 109: Pos(0.477461, 0.103404, -0.084805) Norm(0.66884, 0.577347, 0.468319) UV(0.893159, 0.58671)
|
||||
V 110: Pos(0.477461, -0.567769, -0.084805) Norm(0.668841, -0.57736, 0.468302) UV(0.97572, 0.277359)
|
||||
V 111: Pos(0.477461, -0.567769, -0.084805) Norm(0.668841, -0.57736, 0.468302) UV(0.893159, 0.58671)
|
||||
V 112: Pos(0.468575, -0.567769, -0.135202) Norm(0.468336, -0.577333, -0.668841) UV(0.412645, 0.553153)
|
||||
V 113: Pos(0.269496, -0.567769, -0.048135) Norm(-0.468336, -0.577333, 0.668841) UV(0.764763, 0.277359)
|
||||
V 114: Pos(0.243142, 0.285422, -0.083115) Norm(-0.577359, 0.577332, 0.577359) UV(0.551589, 0.228587)
|
||||
V 115: Pos(0.347237, 0.285422, -0.083115) Norm(0.577358, 0.577347, 0.577347) UV(0.658221, 0.228563)
|
||||
V 116: Pos(0.347237, 0.221328, -0.083115) Norm(0.577359, -0.577332, 0.577359) UV(0.573298, 0.228588)
|
||||
V 117: Pos(0.243142, 0.221328, -0.083115) Norm(-0.577358, -0.577347, 0.577347) UV(0.614763, 0.17729)
|
||||
V 118: Pos(0.347237, 0.221328, -0.127209) Norm(0.577358, -0.577347, -0.577347) UV(0.583176, 0.17729)
|
||||
|
||||
===Triangles (Indices): 60
|
||||
Tri: 0 1 2
|
||||
Tri: 3 4 5
|
||||
Tri: 6 7 8
|
||||
Tri: 9 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 15 16 17
|
||||
Tri: 18 19 20
|
||||
Tri: 21 22 23
|
||||
Tri: 24 25 26
|
||||
Tri: 27 28 29
|
||||
Tri: 30 31 32
|
||||
Tri: 33 34 35
|
||||
Tri: 36 37 38
|
||||
Tri: 39 40 41
|
||||
Tri: 42 43 44
|
||||
Tri: 45 46 47
|
||||
Tri: 48 49 50
|
||||
Tri: 51 52 53
|
||||
Tri: 54 55 56
|
||||
Tri: 57 58 59
|
||||
Tri: 60 61 62
|
||||
Tri: 63 64 65
|
||||
Tri: 66 67 68
|
||||
Tri: 69 70 71
|
||||
Tri: 72 73 74
|
||||
Tri: 75 76 77
|
||||
Tri: 78 79 80
|
||||
Tri: 81 82 83
|
||||
Tri: 84 85 86
|
||||
Tri: 87 88 89
|
||||
Tri: 0 90 1
|
||||
Tri: 3 91 4
|
||||
Tri: 6 92 7
|
||||
Tri: 9 93 10
|
||||
Tri: 12 94 13
|
||||
Tri: 15 95 16
|
||||
Tri: 18 96 19
|
||||
Tri: 21 97 22
|
||||
Tri: 24 98 25
|
||||
Tri: 27 99 28
|
||||
Tri: 30 100 31
|
||||
Tri: 33 101 34
|
||||
Tri: 36 102 37
|
||||
Tri: 39 103 40
|
||||
Tri: 42 104 43
|
||||
Tri: 45 105 46
|
||||
Tri: 48 106 49
|
||||
Tri: 51 107 52
|
||||
Tri: 54 108 55
|
||||
Tri: 57 109 58
|
||||
Tri: 60 110 61
|
||||
Tri: 63 111 64
|
||||
Tri: 66 112 67
|
||||
Tri: 69 113 70
|
||||
Tri: 72 114 73
|
||||
Tri: 75 115 76
|
||||
Tri: 78 116 79
|
||||
Tri: 81 117 82
|
||||
Tri: 84 118 85
|
||||
Tri: 87 117 88
|
||||
@@ -0,0 +1,172 @@
|
||||
===Vertices (Split by UV/Normal): 109
|
||||
V 0: Pos(0.02036, -0.316218, 0.03328) Norm(-0.577372, -0.57734, 0.57734) UV(0.582523, 0.71522)
|
||||
V 1: Pos(0.02036, -0.14665, -0.12644) Norm(-0.577372, 0.57734, -0.57734) UV(0.672618, 0.618748)
|
||||
V 2: Pos(0.02036, -0.316218, -0.12644) Norm(-0.577341, -0.577361, -0.577348) UV(0.672618, 0.715234)
|
||||
V 3: Pos(0.02036, -0.14665, 0.03328) Norm(-0.577341, 0.577354, 0.577356) UV(0.0, 0.850964)
|
||||
V 4: Pos(0.132668, -0.14665, -0.12644) Norm(0.543117, 0.593412, -0.594042) UV(0.063904, 0.941846)
|
||||
V 5: Pos(0.02036, -0.14665, -0.12644) Norm(-0.577372, 0.57734, -0.57734) UV(0.0, 0.941846)
|
||||
V 6: Pos(0.14154, -0.14665, 0.03328) Norm(0.584307, 0.579838, 0.567779) UV(0.672958, 0.522251)
|
||||
V 7: Pos(0.14154, -0.316218, -0.12644) Norm(0.583322, -0.568894, -0.579737) UV(0.582523, 0.618734)
|
||||
V 8: Pos(0.132668, -0.14665, -0.12644) Norm(0.543117, 0.593412, -0.594042) UV(0.582929, 0.522217)
|
||||
V 9: Pos(0.14154, -0.316218, 0.03328) Norm(0.577339, -0.577359, 0.577353) UV(0.068952, 0.850964)
|
||||
V 10: Pos(0.02036, -0.316218, -0.12644) Norm(-0.577341, -0.577361, -0.577348) UV(0.137905, 0.941846)
|
||||
V 11: Pos(0.14154, -0.316218, -0.12644) Norm(0.583322, -0.568894, -0.579737) UV(0.068952, 0.941846)
|
||||
V 12: Pos(0.132668, -0.14665, -0.12644) Norm(0.543117, 0.593412, -0.594042) UV(0.646367, 0.811719)
|
||||
V 13: Pos(0.02036, -0.316218, -0.12644) Norm(-0.577341, -0.577361, -0.577348) UV(0.582523, 0.908205)
|
||||
V 14: Pos(0.02036, -0.14665, -0.12644) Norm(-0.577372, 0.57734, -0.57734) UV(0.582523, 0.811719)
|
||||
V 15: Pos(0.02036, -0.14665, 0.03328) Norm(-0.577341, 0.577354, 0.577356) UV(0.582523, 0.811719)
|
||||
V 16: Pos(0.14154, -0.316218, 0.03328) Norm(0.577339, -0.577359, 0.577353) UV(0.65141, 0.715234)
|
||||
V 17: Pos(0.14154, -0.14665, 0.03328) Norm(0.584307, 0.579838, 0.567779) UV(0.65141, 0.811719)
|
||||
V 18: Pos(-0.614892, 0.342792, 0.284424) Norm(-0.57734, -0.577355, 0.577355) UV(0.680198, 0.763823)
|
||||
V 19: Pos(-0.614892, 0.561483, -0.134268) Norm(-0.57734, 0.577355, -0.577355) UV(0.804635, 0.999981)
|
||||
V 20: Pos(-0.614892, 0.342792, -0.134268) Norm(-0.577355, -0.577371, -0.577325) UV(0.680198, 1.0)
|
||||
V 21: Pos(-0.614892, 0.561483, 0.284424) Norm(-0.577355, 0.577371, 0.577325) UV(0.0, 0.850964)
|
||||
V 22: Pos(-0.0362, 0.561483, -0.134268) Norm(0.577355, 0.577371, -0.577325) UV(0.238239, 0.521684)
|
||||
V 23: Pos(-0.614892, 0.561483, -0.134268) Norm(-0.57734, 0.577355, -0.577355) UV(0.238239, 0.850964)
|
||||
V 24: Pos(-0.0362, 0.561483, 0.284424) Norm(0.57734, 0.577355, 0.577355) UV(0.804635, 0.658012)
|
||||
V 25: Pos(-0.0362, 0.342792, -0.134268) Norm(0.57734, -0.577355, -0.577355) UV(0.929069, 0.895159)
|
||||
V 26: Pos(-0.0362, 0.561483, -0.134268) Norm(0.577355, 0.577371, -0.577325) UV(0.804635, 0.895235)
|
||||
V 27: Pos(-0.0362, 0.342792, 0.284424) Norm(0.577355, -0.577371, 0.577325) UV(0.344284, 0.851497)
|
||||
V 28: Pos(-0.614892, 0.342792, -0.134268) Norm(-0.577355, -0.577371, -0.577325) UV(0.582523, 0.522217)
|
||||
V 29: Pos(-0.0362, 0.342792, -0.134268) Norm(0.57734, -0.577355, -0.577355) UV(0.582523, 0.851497)
|
||||
V 30: Pos(-0.0362, 0.561483, -0.134268) Norm(0.577355, 0.577371, -0.577325) UV(0.929072, 0.657935)
|
||||
V 31: Pos(-0.614892, 0.342792, -0.134268) Norm(-0.577355, -0.577371, -0.577325) UV(0.804635, 0.328968)
|
||||
V 32: Pos(-0.614892, 0.561483, -0.134268) Norm(-0.57734, 0.577355, -0.577355) UV(0.929072, 0.328968)
|
||||
V 33: Pos(-0.614892, 0.561483, 0.284424) Norm(-0.577355, 0.577371, 0.577325) UV(0.804635, 0.0)
|
||||
V 34: Pos(-0.0362, 0.342792, 0.284424) Norm(0.577355, -0.577371, 0.577325) UV(0.929072, 0.328968)
|
||||
V 35: Pos(-0.0362, 0.561483, 0.284424) Norm(0.57734, 0.577355, 0.577355) UV(0.804635, 0.328968)
|
||||
V 36: Pos(-0.04492, -0.422368, 0.425114) Norm(-0.970839, -0.041701, -0.236076) UV(0.062426, 0.358244)
|
||||
V 37: Pos(0.154366, -0.062626, -0.04817) Norm(0.071923, 0.11404, -0.990869) UV(0.344284, 0.15346)
|
||||
V 38: Pos(0.154366, -0.422368, -0.04817) Norm(0.073067, -0.106497, -0.991625) UV(0.344284, 0.358155)
|
||||
V 39: Pos(-0.04492, -0.062626, 0.425114) Norm(-0.970251, 0.045613, -0.237765) UV(0.062426, 0.153549)
|
||||
V 40: Pos(0.16953, 0.207044, 0.020093) Norm(0.24987, 0.873883, -0.417005) UV(0.306911, -0.0)
|
||||
V 41: Pos(-0.049131, 0.207044, 0.535225) Norm(-0.195195, 0.782445, 0.591336) UV(0.344284, 0.522217)
|
||||
V 42: Pos(0.16953, -0.709618, 0.020093) Norm(0.2492, -0.870253, -0.424923) UV(0.647624, 0.0)
|
||||
V 43: Pos(0.16953, 0.207044, 0.020093) Norm(0.24987, 0.873883, -0.417005) UV(0.647624, 0.521575)
|
||||
V 44: Pos(-0.049131, -0.709618, 0.535225) Norm(-0.196826, -0.783515, 0.589376) UV(0.0, 0.521684)
|
||||
V 45: Pos(0.16953, -0.709618, 0.020093) Norm(0.2492, -0.870253, -0.424923) UV(0.306911, 0.521586)
|
||||
V 46: Pos(0.154366, -0.422368, -0.04817) Norm(0.073067, -0.106497, -0.991625) UV(0.685505, 0.163524)
|
||||
V 47: Pos(0.154366, -0.062626, -0.04817) Norm(0.071923, 0.11404, -0.990869) UV(0.685505, 0.368215)
|
||||
V 48: Pos(-0.049131, 0.207044, 0.535225) Norm(-0.195195, 0.782445, 0.591336) UV(0.0, 9.8e-05)
|
||||
V 49: Pos(0.269496, -0.567769, -0.048135) Norm(-0.468336, -0.577333, 0.668841) UV(0.266327, 0.903586)
|
||||
V 50: Pos(0.26061, 0.103404, -0.098532) Norm(-0.66884, 0.577347, -0.468319) UV(0.294091, 0.521694)
|
||||
V 51: Pos(0.26061, -0.567769, -0.098532) Norm(-0.668841, -0.57736, -0.468302) UV(0.294091, 0.903596)
|
||||
V 52: Pos(0.269496, 0.103404, -0.048135) Norm(-0.468342, 0.577346, 0.668826) UV(0.323209, 0.521684)
|
||||
V 53: Pos(0.468575, 0.103404, -0.135202) Norm(0.468342, 0.577346, -0.668826) UV(0.294091, 0.641843)
|
||||
V 54: Pos(0.26061, 0.103404, -0.098532) Norm(-0.66884, 0.577347, -0.468319) UV(0.294091, 0.521684)
|
||||
V 55: Pos(0.477461, 0.103404, -0.084805) Norm(0.66884, 0.577347, 0.468319) UV(0.238239, 0.903578)
|
||||
V 56: Pos(0.468575, -0.567769, -0.135202) Norm(0.468336, -0.577333, -0.668841) UV(0.266327, 0.521735)
|
||||
V 57: Pos(0.468575, 0.103404, -0.135202) Norm(0.468342, 0.577346, -0.668826) UV(0.266327, 0.903629)
|
||||
V 58: Pos(0.468575, -0.567769, -0.135202) Norm(0.468336, -0.577333, -0.668841) UV(0.323209, 0.762002)
|
||||
V 59: Pos(0.269496, -0.567769, -0.048135) Norm(-0.468336, -0.577333, 0.668841) UV(0.294091, 0.641843)
|
||||
V 60: Pos(0.26061, -0.567769, -0.098532) Norm(-0.668841, -0.57736, -0.468302) UV(0.323209, 0.641843)
|
||||
V 61: Pos(0.468575, 0.103404, -0.135202) Norm(0.468342, 0.577346, -0.668826) UV(0.804635, 0.381902)
|
||||
V 62: Pos(0.26061, -0.567769, -0.098532) Norm(-0.668841, -0.57736, -0.468302) UV(0.685505, 0.763804)
|
||||
V 63: Pos(0.26061, 0.103404, -0.098532) Norm(-0.66884, 0.577347, -0.468319) UV(0.685505, 0.381902)
|
||||
V 64: Pos(0.269496, 0.103404, -0.048135) Norm(-0.468342, 0.577346, 0.668826) UV(0.685505, 0.381902)
|
||||
V 65: Pos(0.477461, -0.567769, -0.084805) Norm(0.668841, -0.57736, 0.468302) UV(0.804635, 0.0)
|
||||
V 66: Pos(0.477461, 0.103404, -0.084805) Norm(0.66884, 0.577347, 0.468319) UV(0.804635, 0.381902)
|
||||
V 67: Pos(0.243142, 0.221328, -0.083115) Norm(-0.577358, -0.577347, 0.577347) UV(0.137905, 0.887434)
|
||||
V 68: Pos(0.243142, 0.285422, -0.127209) Norm(-0.577358, 0.577347, -0.577347) UV(0.162777, 0.850968)
|
||||
V 69: Pos(0.243142, 0.221328, -0.127209) Norm(-0.577359, -0.577332, -0.577359) UV(0.162777, 0.887438)
|
||||
V 70: Pos(0.243142, 0.285422, -0.083115) Norm(-0.577359, 0.577332, 0.577359) UV(0.65141, 0.774464)
|
||||
V 71: Pos(0.347237, 0.285422, -0.127209) Norm(0.577359, 0.577332, -0.577359) UV(0.6765, 0.715234)
|
||||
V 72: Pos(0.243142, 0.285422, -0.127209) Norm(-0.577358, 0.577347, -0.577347) UV(0.6765, 0.774464)
|
||||
V 73: Pos(0.347237, 0.285422, -0.083115) Norm(0.577358, 0.577347, 0.577347) UV(0.65141, 0.870164)
|
||||
V 74: Pos(0.347237, 0.221328, -0.127209) Norm(0.577358, -0.577347, -0.577347) UV(0.676393, 0.83371)
|
||||
V 75: Pos(0.347237, 0.285422, -0.127209) Norm(0.577359, 0.577332, -0.577359) UV(0.676393, 0.870179)
|
||||
V 76: Pos(0.347237, 0.221328, -0.083115) Norm(0.577359, -0.577332, 0.577359) UV(0.65141, 0.833694)
|
||||
V 77: Pos(0.243142, 0.221328, -0.127209) Norm(-0.577359, -0.577332, -0.577359) UV(0.6765, 0.774464)
|
||||
V 78: Pos(0.347237, 0.221328, -0.127209) Norm(0.577358, -0.577347, -0.577347) UV(0.6765, 0.833694)
|
||||
V 79: Pos(0.347237, 0.285422, -0.127209) Norm(0.577359, 0.577332, -0.577359) UV(0.330561, 0.821176)
|
||||
V 80: Pos(0.243142, 0.221328, -0.127209) Norm(-0.577359, -0.577332, -0.577359) UV(0.294091, 0.762002)
|
||||
V 81: Pos(0.243142, 0.285422, -0.127209) Norm(-0.577358, 0.577347, -0.577347) UV(0.330561, 0.762002)
|
||||
V 82: Pos(0.243142, 0.285422, -0.083115) Norm(-0.577359, 0.577332, 0.577359) UV(0.294091, 0.821176)
|
||||
V 83: Pos(0.347237, 0.221328, -0.083115) Norm(0.577359, -0.577332, 0.577359) UV(0.330561, 0.880351)
|
||||
V 84: Pos(0.347237, 0.285422, -0.083115) Norm(0.577358, 0.577347, 0.577347) UV(0.294091, 0.880351)
|
||||
V 85: Pos(0.02036, -0.14665, 0.03328) Norm(-0.577341, 0.577354, 0.577356) UV(0.582523, 0.618734)
|
||||
V 86: Pos(0.14154, -0.14665, 0.03328) Norm(0.584307, 0.579838, 0.567779) UV(0.068952, 0.850964)
|
||||
V 87: Pos(0.14154, -0.316218, 0.03328) Norm(0.577339, -0.577359, 0.577353) UV(0.673017, 0.618734)
|
||||
V 88: Pos(0.02036, -0.316218, 0.03328) Norm(-0.577372, -0.57734, 0.57734) UV(0.137905, 0.850964)
|
||||
V 89: Pos(0.14154, -0.316218, -0.12644) Norm(0.583322, -0.568894, -0.579737) UV(0.65141, 0.908205)
|
||||
V 90: Pos(0.02036, -0.316218, 0.03328) Norm(-0.577372, -0.57734, 0.57734) UV(0.582523, 0.715234)
|
||||
V 91: Pos(-0.614892, 0.561483, 0.284424) Norm(-0.577355, 0.577371, 0.577325) UV(0.804635, 0.763804)
|
||||
V 92: Pos(-0.0362, 0.561483, 0.284424) Norm(0.57734, 0.577355, 0.577355) UV(0.0, 0.521684)
|
||||
V 93: Pos(-0.0362, 0.342792, 0.284424) Norm(0.577355, -0.577371, 0.577325) UV(0.929069, 0.657935)
|
||||
V 94: Pos(-0.614892, 0.342792, 0.284424) Norm(-0.57734, -0.577355, 0.577355) UV(0.344284, 0.522217)
|
||||
V 95: Pos(-0.0362, 0.342792, -0.134268) Norm(0.57734, -0.577355, -0.577355) UV(0.804635, 0.657935)
|
||||
V 96: Pos(-0.614892, 0.342792, 0.284424) Norm(-0.57734, -0.577355, 0.577355) UV(0.929072, 0.0)
|
||||
V 97: Pos(-0.049131, -0.709618, 0.535225) Norm(-0.196826, -0.783515, 0.589376) UV(0.344284, 0.000642)
|
||||
V 98: Pos(0.269496, 0.103404, -0.048135) Norm(-0.468342, 0.577346, 0.668826) UV(0.266327, 0.521684)
|
||||
V 99: Pos(0.477461, 0.103404, -0.084805) Norm(0.66884, 0.577347, 0.468319) UV(0.323209, 0.641843)
|
||||
V 100: Pos(0.477461, -0.567769, -0.084805) Norm(0.668841, -0.57736, 0.468302) UV(0.238239, 0.521684)
|
||||
V 101: Pos(0.477461, -0.567769, -0.084805) Norm(0.668841, -0.57736, 0.468302) UV(0.294091, 0.762002)
|
||||
V 102: Pos(0.468575, -0.567769, -0.135202) Norm(0.468336, -0.577333, -0.668841) UV(0.804635, 0.763804)
|
||||
V 103: Pos(0.269496, -0.567769, -0.048135) Norm(-0.468336, -0.577333, 0.668841) UV(0.685505, 0.0)
|
||||
V 104: Pos(0.243142, 0.285422, -0.083115) Norm(-0.577359, 0.577332, 0.577359) UV(0.137905, 0.850964)
|
||||
V 105: Pos(0.347237, 0.285422, -0.083115) Norm(0.577358, 0.577347, 0.577347) UV(0.65141, 0.715234)
|
||||
V 106: Pos(0.243142, 0.221328, -0.083115) Norm(-0.577358, -0.577347, 0.577347) UV(0.65141, 0.774464)
|
||||
V 107: Pos(0.347237, 0.221328, -0.127209) Norm(0.577358, -0.577347, -0.577347) UV(0.294091, 0.821176)
|
||||
V 108: Pos(0.243142, 0.221328, -0.083115) Norm(-0.577358, -0.577347, 0.577347) UV(0.330561, 0.821176)
|
||||
|
||||
===Triangles (Indices): 60
|
||||
Tri: 0 1 2
|
||||
Tri: 3 4 5
|
||||
Tri: 6 7 8
|
||||
Tri: 9 10 11
|
||||
Tri: 12 13 14
|
||||
Tri: 15 16 17
|
||||
Tri: 18 19 20
|
||||
Tri: 21 22 23
|
||||
Tri: 24 25 26
|
||||
Tri: 27 28 29
|
||||
Tri: 30 31 32
|
||||
Tri: 33 34 35
|
||||
Tri: 36 37 38
|
||||
Tri: 39 40 37
|
||||
Tri: 41 42 43
|
||||
Tri: 44 38 45
|
||||
Tri: 43 46 47
|
||||
Tri: 39 44 48
|
||||
Tri: 49 50 51
|
||||
Tri: 52 53 54
|
||||
Tri: 55 56 57
|
||||
Tri: 58 59 60
|
||||
Tri: 61 62 63
|
||||
Tri: 64 65 66
|
||||
Tri: 67 68 69
|
||||
Tri: 70 71 72
|
||||
Tri: 73 74 75
|
||||
Tri: 76 77 78
|
||||
Tri: 79 80 81
|
||||
Tri: 82 83 84
|
||||
Tri: 0 85 1
|
||||
Tri: 3 86 4
|
||||
Tri: 6 87 7
|
||||
Tri: 9 88 10
|
||||
Tri: 12 89 13
|
||||
Tri: 15 90 16
|
||||
Tri: 18 91 19
|
||||
Tri: 21 92 22
|
||||
Tri: 24 93 25
|
||||
Tri: 27 94 28
|
||||
Tri: 30 95 31
|
||||
Tri: 33 96 34
|
||||
Tri: 36 39 37
|
||||
Tri: 39 48 40
|
||||
Tri: 41 97 42
|
||||
Tri: 44 36 38
|
||||
Tri: 43 42 46
|
||||
Tri: 39 36 44
|
||||
Tri: 49 98 50
|
||||
Tri: 52 99 53
|
||||
Tri: 55 100 56
|
||||
Tri: 58 101 59
|
||||
Tri: 61 102 62
|
||||
Tri: 64 103 65
|
||||
Tri: 67 104 68
|
||||
Tri: 70 105 71
|
||||
Tri: 73 76 74
|
||||
Tri: 76 106 77
|
||||
Tri: 79 107 80
|
||||
Tri: 82 108 83
|
||||
Binary file not shown.
@@ -7,6 +7,7 @@
|
||||
#include "GameConstants.h"
|
||||
#include "Environment.h"
|
||||
|
||||
|
||||
namespace ZL {
|
||||
|
||||
const float ATTACK_COOLDOWN_TIME = 1.6f;
|
||||
@@ -94,6 +95,15 @@ Eigen::Vector3f Character::getCurrentNavigationTarget() const
|
||||
return walkTarget;
|
||||
}
|
||||
|
||||
void Character::stopInPlace()
|
||||
{
|
||||
walkTarget = Eigen::Vector3f(position.x(), 0.f, position.z());
|
||||
requestedWalkTarget = walkTarget;
|
||||
pathWaypoints.clear();
|
||||
currentWaypointIndex = 0;
|
||||
onArrivedCallback = nullptr;
|
||||
}
|
||||
|
||||
void Character::forceReplan()
|
||||
{
|
||||
if (!pathPlanner) {
|
||||
@@ -343,6 +353,18 @@ void Character::update(int64_t deltaMs) {
|
||||
}
|
||||
|
||||
|
||||
if (!isPlayer && hp > 0.f && battle_state == 0 && maxHomeDrift > 0.f) {
|
||||
homeDriftCheckTimer -= static_cast<float>(deltaMs) / 1000.f;
|
||||
if (homeDriftCheckTimer <= 0.f) {
|
||||
homeDriftCheckTimer = HOME_DRIFT_CHECK_INTERVAL;
|
||||
Eigen::Vector3f toHome = homePosition - position;
|
||||
toHome.y() = 0.f;
|
||||
if (!isMoving() && toHome.norm() > maxHomeDrift) {
|
||||
setTarget(homePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hitSparkEmitter.isConfigured()) {
|
||||
hitSparkEmitter.update(static_cast<float>(deltaMs));
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ public:
|
||||
const Eigen::Vector3f& getRequestedWalkTarget() const { return requestedWalkTarget; }
|
||||
Eigen::Vector3f getCurrentNavigationTarget() const;
|
||||
void forceReplan();
|
||||
// Cancels any active walk target and clears waypoints so the character
|
||||
// stays at its current position. Used when an external push moves the
|
||||
// character and we don't want it to walk back to where it was.
|
||||
void stopInPlace();
|
||||
|
||||
|
||||
// attackDirection is a world-space horizontal vector pointing from the
|
||||
@@ -110,6 +114,13 @@ public:
|
||||
float hp = 200.f;
|
||||
float initialHp = 200.f;
|
||||
|
||||
// The position the NPC should return to if pushed too far away.
|
||||
// Initialised to the spawn position; updated whenever a script walk command is issued.
|
||||
Eigen::Vector3f homePosition = Eigen::Vector3f::Zero();
|
||||
// Maximum allowed drift from homePosition before a return walk is triggered (metres).
|
||||
// Set to 0 to disable drift recovery for this character.
|
||||
float maxHomeDrift = 3.0f;
|
||||
|
||||
|
||||
int battle_state = 0;
|
||||
bool resetAnim = false;
|
||||
@@ -163,6 +174,9 @@ private:
|
||||
|
||||
static constexpr float WALK_THRESHOLD = 0.05f;
|
||||
static constexpr float TARGET_REPLAN_THRESHOLD = 0.25f;
|
||||
static constexpr float HOME_DRIFT_CHECK_INTERVAL = 5.0f;
|
||||
|
||||
float homeDriftCheckTimer = HOME_DRIFT_CHECK_INTERVAL;
|
||||
|
||||
// Returns the animation state to actually play/draw, falling back to IDLE
|
||||
// if the requested state has no loaded animation.
|
||||
|
||||
+1
-3
@@ -233,9 +233,7 @@ namespace ZL
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports.json";
|
||||
uniInteriorParams.teleportsJsonPath = "resources/config2/teleports_uni_interior.json";
|
||||
uniInteriorParams.triggerZonesJsonPath = "resources/config2/trigger_zones_uni_interior.json";
|
||||
uniInteriorParams.scriptPath = "resources/start_uni_interior.lua";
|
||||
uniInteriorParams.interactiveObjectsJsonPath = "resources/config2/interactive_objects_uni_interior.json";
|
||||
|
||||
+42
-4
@@ -111,6 +111,7 @@ namespace ZL
|
||||
npcs = GameObjectLoader::loadAndCreate_Npcs(params.npcsJsonPath, CONST_ZIP_FILE);
|
||||
for (auto& npc : npcs) {
|
||||
if (!npc) continue;
|
||||
npc->homePosition = Eigen::Vector3f(npc->position.x(), 0.f, npc->position.z());
|
||||
if (npc->canAttack) {
|
||||
npc->setupHitSparks(sparkTexture);
|
||||
npc->attackTarget = player.get();
|
||||
@@ -297,6 +298,7 @@ namespace ZL
|
||||
const auto addCharacter = [&](const Character* other) {
|
||||
if (!other || other == self) return;
|
||||
if (other->hp <= 0.f || !other->enabled) return;
|
||||
if (other->isMoving()) return;
|
||||
|
||||
if (distancePointToSegmentXZ(other->position, start, end) > kDynamicObstacleInfluenceDist) {
|
||||
return;
|
||||
@@ -304,7 +306,7 @@ namespace ZL
|
||||
|
||||
PathFinder::DynamicObstacle obs;
|
||||
obs.position = Eigen::Vector3f(other->position.x(), navigation->getFloorY(), other->position.z());
|
||||
obs.radius = (std::max)(0.0f, other->collisionRadius);
|
||||
obs.radius = (std::max)(0.0f, other->collisionRadius * 0.6f);
|
||||
dynamicObstacles.push_back(obs);
|
||||
};
|
||||
|
||||
@@ -313,7 +315,7 @@ namespace ZL
|
||||
addCharacter(npc.get());
|
||||
}
|
||||
|
||||
return navigation->findPath(start, end, dynamicObstacles);
|
||||
return navigation->findPathToNearest(start, end, dynamicObstacles);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -981,6 +983,30 @@ namespace ZL
|
||||
return navigation->setAreaAvailable(areaName, available);
|
||||
}
|
||||
|
||||
void Location::nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom)
|
||||
{
|
||||
if (!standing || standing->isPlayer) return;
|
||||
if (!navigation || !navigation->isReady()) return;
|
||||
|
||||
static constexpr float kNudgeDist = 1.2f;
|
||||
|
||||
Eigen::Vector3f dir = standing->position - awayFrom;
|
||||
dir.y() = 0.f;
|
||||
if (dir.norm() < 1e-3f) dir = Eigen::Vector3f(1.f, 0.f, 0.f);
|
||||
else dir.normalize();
|
||||
|
||||
const float angles[] = { 0.f, static_cast<float>(M_PI * 0.5), static_cast<float>(-M_PI * 0.5), static_cast<float>(M_PI) };
|
||||
for (float angle : angles) {
|
||||
Eigen::Vector3f rotated = Eigen::AngleAxisf(angle, Eigen::Vector3f::UnitY()) * dir;
|
||||
Eigen::Vector3f candidate = standing->position + rotated * kNudgeDist;
|
||||
candidate.y() = 0.f;
|
||||
if (navigation->isWalkable(candidate)) {
|
||||
standing->setTarget(candidate);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Location::resolveCharacterCollisions()
|
||||
{
|
||||
std::vector<Character*> characters;
|
||||
@@ -1039,6 +1065,9 @@ namespace ZL
|
||||
newB.z() += normal.y() * push;
|
||||
newB.y() = 0.f;
|
||||
|
||||
const bool aWasMoving = a->isMoving();
|
||||
const bool bWasMoving = b->isMoving();
|
||||
|
||||
if (navigation && navigation->isReady()) {
|
||||
const bool aOk = navigation->isWalkable(newA);
|
||||
const bool bOk = navigation->isWalkable(newB);
|
||||
@@ -1058,6 +1087,15 @@ namespace ZL
|
||||
a->position = newA;
|
||||
b->position = newB;
|
||||
}
|
||||
|
||||
if (a->isPlayer && !aWasMoving) a->stopInPlace();
|
||||
if (b->isPlayer && !bWasMoving) b->stopInPlace();
|
||||
|
||||
if (aWasMoving && !bWasMoving) {
|
||||
nudgeCharacterAside(b, a->position);
|
||||
} else if (bWasMoving && !aWasMoving) {
|
||||
nudgeCharacterAside(a, b->position);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1066,8 +1104,8 @@ namespace ZL
|
||||
void Location::updateDynamicReplans(int64_t deltaMs)
|
||||
{
|
||||
static constexpr float kMovedEps = 0.05f;
|
||||
static constexpr float kReplanTriggerDist = 1.1f;
|
||||
static constexpr int64_t kReplanCooldownMs = 300;
|
||||
static constexpr float kReplanTriggerDist = 1.8f;
|
||||
static constexpr int64_t kReplanCooldownMs = 500;
|
||||
|
||||
for (auto it = replanCooldownRemainingMs.begin(); it != replanCooldownRemainingMs.end();) {
|
||||
it->second -= deltaMs;
|
||||
|
||||
@@ -151,6 +151,7 @@ namespace ZL
|
||||
void loadTeleportZones(const std::string& jsonPath, const char* zipFile);
|
||||
void loadTriggerZones(const std::string& jsonPath, const char* zipFile);
|
||||
void updateTriggerZones(const Eigen::Vector3f& playerPos);
|
||||
void nudgeCharacterAside(Character* standing, const Eigen::Vector3f& awayFrom);
|
||||
|
||||
std::unordered_map<Character*, Eigen::Vector3f> lastCharacterPositions;
|
||||
std::unordered_map<Character*, int64_t> replanCooldownRemainingMs;
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace ZL {
|
||||
}
|
||||
};
|
||||
}
|
||||
npcs[index]->homePosition = Eigen::Vector3f(x, 0.f, z);
|
||||
npcs[index]->setTarget(Eigen::Vector3f(x, y, z), std::move(cb));
|
||||
});
|
||||
|
||||
@@ -104,7 +105,7 @@ namespace ZL {
|
||||
api.set_function("set_object_rotation", [game](const std::string& objectName, float value) {
|
||||
for (auto& intObj : game->interactiveObjects) {
|
||||
if (intObj.loadedObject.name == objectName) {
|
||||
intObj.rotationY = value;
|
||||
intObj.rotationY = value * static_cast<float>(M_PI) / 180.f;
|
||||
std::cout << "[script] set_object_rotation: " << objectName << " " << value << std::endl;
|
||||
return;
|
||||
}
|
||||
@@ -316,7 +317,7 @@ namespace ZL {
|
||||
npcs[index]->setTarget(pos);
|
||||
});
|
||||
|
||||
// npc_set_rotation(index, angle) — sets NPC facing angle around Y axis (radians).
|
||||
// npc_set_rotation(index, angle) — sets NPC facing angle around Y axis (degrees).
|
||||
api.set_function("npc_set_rotation",
|
||||
[game](int index, float angle) {
|
||||
auto& npcs = game->npcs;
|
||||
@@ -324,8 +325,9 @@ namespace ZL {
|
||||
std::cerr << "[script] npc_set_rotation: index " << index << " out of range\n";
|
||||
return;
|
||||
}
|
||||
npcs[index]->facingAngle = angle;
|
||||
npcs[index]->targetFacingAngle = angle;
|
||||
const float rad = angle * static_cast<float>(M_PI) / 180.f;
|
||||
npcs[index]->facingAngle = rad;
|
||||
npcs[index]->targetFacingAngle = rad;
|
||||
});
|
||||
|
||||
// set_npc_enabled(index, enabled)
|
||||
|
||||
@@ -119,13 +119,14 @@ namespace ZL {
|
||||
else
|
||||
obj.mesh.data = LoadFromTextFile02(data.meshPath, zipPath);
|
||||
|
||||
constexpr float kDeg2Rad = static_cast<float>(M_PI) / 180.0f;
|
||||
Eigen::Quaternionf rot = Eigen::Quaternionf::Identity();
|
||||
if (data.rotationX != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationX, Eigen::Vector3f::UnitX())) * rot;
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationX * kDeg2Rad, Eigen::Vector3f::UnitX())) * rot;
|
||||
if (data.rotationY != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationY, Eigen::Vector3f::UnitY())) * rot;
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationY * kDeg2Rad, Eigen::Vector3f::UnitY())) * rot;
|
||||
if (data.rotationZ != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationZ, Eigen::Vector3f::UnitZ())) * rot;
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationZ * kDeg2Rad, Eigen::Vector3f::UnitZ())) * rot;
|
||||
obj.mesh.data.RotateByMatrix(rot.toRotationMatrix());
|
||||
|
||||
if (data.scale != 1.0f)
|
||||
@@ -262,7 +263,7 @@ namespace ZL {
|
||||
data.modelCorrectionRotX = item.value("modelCorrectionRotX", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||
data.modelCorrectionRotY = item.value("modelCorrectionRotY", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||
data.modelCorrectionRotZ = item.value("modelCorrectionRotZ", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||
data.facingAngle = item.value("facingAngle", 0.0f);
|
||||
data.facingAngle = item.value("facingAngle", 0.0f) * static_cast<float>(M_PI) / 180.0f;
|
||||
data.hp = item.value("hp", 100.0f);
|
||||
data.canAttack = item.value("canAttack", false);
|
||||
data.enabled = item.value("enabled", true);
|
||||
|
||||
@@ -397,6 +397,124 @@ std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<Eigen::Vector3f> PathFinder::findNearestReachableImpl(
|
||||
const Eigen::Vector3f& start,
|
||||
const Eigen::Vector3f& end,
|
||||
const std::vector<unsigned char>& walkableGrid) const
|
||||
{
|
||||
if (!ready || walkableGrid.empty()) return {};
|
||||
|
||||
Cell startCell;
|
||||
Cell endCell;
|
||||
if (!findNearestWalkableCell(start, startCell, walkableGrid)) return {};
|
||||
if (!findNearestWalkableCell(end, endCell, walkableGrid)) return {};
|
||||
|
||||
struct QueueNode {
|
||||
int index = 0;
|
||||
float priority = 0.0f;
|
||||
bool operator<(const QueueNode& other) const { return priority > other.priority; }
|
||||
};
|
||||
|
||||
const int cellCount = gridWidth * gridDepth;
|
||||
std::vector<float> cost(static_cast<size_t>(cellCount), INF_COST);
|
||||
std::vector<int> cameFrom(static_cast<size_t>(cellCount), -1);
|
||||
std::priority_queue<QueueNode> open;
|
||||
|
||||
const int startIndex = indexOf(startCell);
|
||||
const int endIndex = indexOf(endCell);
|
||||
|
||||
cost[static_cast<size_t>(startIndex)] = 0.f;
|
||||
open.push({ startIndex, 0.f });
|
||||
|
||||
int bestIndex = startIndex;
|
||||
float bestDist = distanceCells(startCell, endCell);
|
||||
|
||||
static const int offsets[8][2] = {
|
||||
{ 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 },
|
||||
{ 1, 1 }, { 1, -1 }, { -1, 1 }, { -1, -1 }
|
||||
};
|
||||
|
||||
while (!open.empty()) {
|
||||
const QueueNode current = open.top();
|
||||
open.pop();
|
||||
|
||||
if (current.index == endIndex) {
|
||||
bestIndex = endIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cost[static_cast<size_t>(current.index)] < current.priority - distanceCells({ current.index % gridWidth, current.index / gridWidth }, endCell)) {
|
||||
continue; // stale entry
|
||||
}
|
||||
|
||||
const Cell currentCell{ current.index % gridWidth, current.index / gridWidth };
|
||||
const float d = distanceCells(currentCell, endCell);
|
||||
if (d < bestDist) {
|
||||
bestDist = d;
|
||||
bestIndex = current.index;
|
||||
}
|
||||
|
||||
for (const auto& offset : offsets) {
|
||||
Cell next{ currentCell.x + offset[0], currentCell.z + offset[1] };
|
||||
if (!isCellWalkable(next, walkableGrid)) continue;
|
||||
|
||||
const bool diagonal = offset[0] != 0 && offset[1] != 0;
|
||||
if (diagonal) {
|
||||
Cell h{ currentCell.x + offset[0], currentCell.z };
|
||||
Cell v{ currentCell.x, currentCell.z + offset[1] };
|
||||
if (!isCellWalkable(h, walkableGrid) || !isCellWalkable(v, walkableGrid)) continue;
|
||||
}
|
||||
|
||||
const int nextIndex = indexOf(next);
|
||||
const float stepCost = diagonal ? 1.41421356f : 1.0f;
|
||||
const float newCost = cost[static_cast<size_t>(current.index)] + stepCost;
|
||||
if (newCost >= cost[static_cast<size_t>(nextIndex)]) continue;
|
||||
|
||||
cost[static_cast<size_t>(nextIndex)] = newCost;
|
||||
cameFrom[static_cast<size_t>(nextIndex)] = current.index;
|
||||
open.push({ nextIndex, newCost + distanceCells(next, endCell) });
|
||||
}
|
||||
}
|
||||
|
||||
if (bestIndex == startIndex) return {};
|
||||
|
||||
std::vector<Cell> cells;
|
||||
for (int cur = bestIndex; cur != -1; cur = cameFrom[static_cast<size_t>(cur)]) {
|
||||
cells.push_back({ cur % gridWidth, cur / gridWidth });
|
||||
if (cur == startIndex) break;
|
||||
}
|
||||
std::reverse(cells.begin(), cells.end());
|
||||
cells = smoothCells(cells, walkableGrid);
|
||||
|
||||
std::vector<Eigen::Vector3f> path;
|
||||
path.reserve(cells.size());
|
||||
for (const Cell& cell : cells) path.push_back(cellCenter(cell));
|
||||
|
||||
if (!path.empty() && (path.front() - Eigen::Vector3f(start.x(), floorY, start.z())).norm() < cellSize * 0.75f)
|
||||
path.erase(path.begin());
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<Eigen::Vector3f> PathFinder::findPathToNearest(
|
||||
const Eigen::Vector3f& start, const Eigen::Vector3f& end) const
|
||||
{
|
||||
auto path = findPath(start, end);
|
||||
if (!path.empty()) return path;
|
||||
return findNearestReachableImpl(start, end, walkable);
|
||||
}
|
||||
|
||||
std::vector<Eigen::Vector3f> PathFinder::findPathToNearest(
|
||||
const Eigen::Vector3f& start, const Eigen::Vector3f& end,
|
||||
const std::vector<DynamicObstacle>& dynamicObstacles) const
|
||||
{
|
||||
auto path = findPath(start, end, dynamicObstacles);
|
||||
if (!path.empty()) return path;
|
||||
path = findPath(start, end);
|
||||
if (!path.empty()) return path;
|
||||
return findNearestReachableImpl(start, end, walkable);
|
||||
}
|
||||
|
||||
bool PathFinder::setAreaAvailable(const std::string& areaName, bool available)
|
||||
{
|
||||
bool found = false;
|
||||
|
||||
@@ -41,6 +41,14 @@ public:
|
||||
const Eigen::Vector3f& end,
|
||||
const std::vector<DynamicObstacle>& dynamicObstacles) const;
|
||||
|
||||
// Like findPath, but when the destination is unreachable the character
|
||||
// walks as close as possible instead of not moving at all.
|
||||
std::vector<Eigen::Vector3f> findPathToNearest(const Eigen::Vector3f& start,
|
||||
const Eigen::Vector3f& end) const;
|
||||
std::vector<Eigen::Vector3f> findPathToNearest(const Eigen::Vector3f& start,
|
||||
const Eigen::Vector3f& end,
|
||||
const std::vector<DynamicObstacle>& dynamicObstacles) const;
|
||||
|
||||
bool setAreaAvailable(const std::string& areaName, bool available);
|
||||
bool isReady() const { return ready; }
|
||||
bool isWalkable(const Eigen::Vector3f& point) const;
|
||||
@@ -95,6 +103,12 @@ private:
|
||||
bool findNearestWalkableCell(const Eigen::Vector3f& point, Cell& out, const std::vector<unsigned char>& walkableGrid) const;
|
||||
bool hasLineOfSight(const Cell& from, const Cell& to, const std::vector<unsigned char>& walkableGrid) const;
|
||||
std::vector<Cell> smoothCells(const std::vector<Cell>& cells, const std::vector<unsigned char>& walkableGrid) const;
|
||||
|
||||
// A* that runs to exhaustion and returns a path to the reachable cell
|
||||
// geometrically closest to end when end itself is unreachable.
|
||||
std::vector<Eigen::Vector3f> findNearestReachableImpl(const Eigen::Vector3f& start,
|
||||
const Eigen::Vector3f& end,
|
||||
const std::vector<unsigned char>& walkableGrid) const;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
Reference in new issue
Block a user