Archived
1
0
Fork 0
This repository has been archived on 2024-02-06. You can view files and clone it, but cannot push or open issues or pull requests.
SMS-Geography/wall.ipynb

303 lines
220 KiB
Text
Raw Normal View History

2022-03-11 21:31:38 +09:00
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "3bb5cd79-48af-47ba-b0c7-6ddb645fdead",
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import struct\n",
"from memorylib import Dolphin\n",
"import matplotlib.pyplot as plt\n",
"from matplotlib import patches\n",
"import itertools\n",
"from collections import defaultdict, Counter\n",
"from shape import Polygon, Polyhedron\n",
"array = np.array\n",
"normalize = lambda x: x/np.linalg.norm(x)\n",
"read_struct = lambda addr, fmt: struct.unpack(fmt, d.read_ram(addr-0x80000000, struct.calcsize(fmt)))\n",
"\n",
"def hook():\n",
" global d, dolphin\n",
" d = dolphin = Dolphin()\n",
" assert dolphin.find_dolphin(), 'Dolphin not found'\n",
" assert dolphin.init_shared_memory(), 'MEM1 not found'\n",
" assert dolphin.read_ram(0, 3).tobytes() == b'GMS', 'Current game is not Sunshine'"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "b2dd3225-7f18-45b8-87f6-bb42279fb2e7",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1868634166512 0x1b3134694f0\n"
]
}
],
"source": [
"hook()"
]
},
{
"cell_type": "markdown",
"id": "194bce4c-80df-41ab-afa5-90616c2b0ca3",
"metadata": {},
"source": [
"## Classes and Functions"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "2f71120c-696f-4eec-b7cc-10f30f1547d1",
"metadata": {},
"outputs": [],
"source": [
"class Surface:\n",
" def __init__(self, surtype, surpara, trntype, unk7, verts, vidxs=None, n=None, c=None):\n",
" self.surtype, self.surpara, self.trntype, self.unk7 = \\\n",
" surtype, surpara, trntype, unk7\n",
" self.vidxs = vidxs\n",
" self.verts = verts\n",
" self.minY = verts[:,1].min()\n",
" self.maxY = verts[:,1].max()\n",
" self.n = normalize(np.cross(verts[1]-verts[0], verts[2]-verts[1])) if n is None else n\n",
" self.c = -np.dot(verts[0], self.n) if c is None else c\n",
" def __repr__(self):\n",
" return 'minY=%.0f maxY=%.0f n=(%5.2f, %5.2f, %5.2f)'%(\n",
" self.minY, self.maxY, *self.n,\n",
" )\n",
" \n",
"def checkList2list(ptr):\n",
" ans = []\n",
" while True:\n",
" ptr, data = read_struct(ptr+4, '>II')\n",
" ans.append(Surface(\n",
" *read_struct(data, '>HHBB'),\n",
" np.array(read_struct(data+0x10, '>9f'), 'f').reshape(3, 3),\n",
" n=np.array(read_struct(data+0x34, '>3f'), 'f'),\n",
" c=d.read_float(data+0x40),\n",
" ))\n",
" if ptr == 0: return ans"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "2c901433-7ee7-4771-9e89-1adc634ea889",
"metadata": {},
"outputs": [],
"source": [
"makeTriPrism = lambda tri0, tri1: Polyhedron([\n",
" *tri0,\n",
" *tri1,\n",
"], [e for i in range(3) for e in [(i, (i+1)%3), (i, i+3), (i+3, (i+1)%3+3)]])\n",
"\n",
"def makeGround(tri, hG=0):\n",
" poly = makeTriPrism(tri.verts+(0,hG,0), tri.verts-(0,108,0))\n",
" poly.clipPlane(p=(0, tri.minY, 0), n=(0, 1, 0))\n",
" return poly\n",
"def makeRoof(tri, hR=82):\n",
" poly = makeTriPrism(tri.verts-(0,hR,0), tri.verts-(0,160,0))\n",
" return poly\n",
"def makeWall(tri, rW=50, dy=30):\n",
" verts = tri.verts - (0, dy, 0)\n",
" n = tri.n\n",
" off = (2*rW,0,0) if abs(n[0])>0.707 else (0,0,2*rW)\n",
" poly = makeTriPrism(verts-off, verts+off)\n",
" poly.clipPlane(p=verts[0], n=n, c=-rW)\n",
" poly.clipPlane(p=verts[0], n=-n, c=-rW)\n",
" return poly\n",
"\n",
"# make collision: Polyhedron[]\n",
"def makeCol(tri, airborne=True):\n",
" ny = tri.n[1]\n",
" if ny > 0.2:\n",
" return [makeGround(tri, 0 if airborne else 100)]\n",
" elif ny < -0.2:\n",
" return [makeRoof(tri, 82 if airborne else 2)]\n",
" else:\n",
" return [\n",
" makeWall(tri, 50, 150),\n",
" makeWall(tri, 50, 30),\n",
" ] if airborne else [\n",
" makeWall(tri, 25, 30),\n",
" makeWall(tri, 50, 60),\n",
" ]"
]
},
{
"cell_type": "markdown",
"id": "17de8a60-e9eb-4507-9aaa-10e2deeba749",
"metadata": {},
"source": [
"## Get data from Dolphin"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "e2b4da62-691e-44ec-bb03-4893d644ec6b",
"metadata": {},
"outputs": [],
"source": [
"# mario\n",
"gpMarioOriginal = 0x8040A378\n",
"ptrMario = d.read_uint32(gpMarioOriginal)\n",
"x, y, z = read_struct(ptrMario+0x10, '>3f')\n",
"\n",
"# get collision data (static collision)\n",
"gpMap = 0x8040A570\n",
"ptrMap = d.read_uint32(gpMap)\n",
"ptrCol = d.read_uint32(ptrMap+0x10)\n",
"xLimit, zLimit, xBlockCount, ptrStCLR = read_struct(ptrCol, '>ffI4x4xI')\n",
"## TBGCheckListRoot[zBlockCount][xBlockCount]\n",
"colOff = int((z+zLimit)//1024*xBlockCount + (x+xLimit)//1024)*36\n",
"## root->ground(12*2).next(4)\n",
"stWalls = checkList2list(d.read_uint32(ptrStCLR+colOff+4+12*2))"
]
},
{
"cell_type": "markdown",
"id": "cbb5f4b3-c5c8-4b95-ba11-5de3b8e05f1c",
"metadata": {},
"source": [
"## Draw wall hitbox"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "5a6ef0d3-76e0-427d-9fb0-c3e5360c9199",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 576x576 with 1 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"yy, airborne = 183, False\n",
"#yy, airborne = y, True\n",
"\n",
"# paras\n",
"margin = 0.05\n",
"\n",
"# vars\n",
"fig, ax = plt.subplots(figsize=(8,8))\n",
"xyMax = np.full(2, -np.inf)\n",
"xyMin = np.full(2, np.inf)\n",
"\n",
"# draw 2 hitboxs (draw in reverse order)\n",
"for ii in [1, 0]:\n",
" polys = [\n",
" (c, wall.n)\n",
" for wall in stWalls\n",
" for c in [makeCol(wall, airborne=airborne)[ii]]\n",
" # FIXME: filtering walls\n",
" if c.verts[:,2].min()<14050\n",
" ]\n",
" # arrow size scale (0.5 if radius is 25)\n",
" amul = 1.0 if airborne or ii == 1 else 0.5\n",
" # draw wall hitboxs (draw in reverse order)\n",
" for poly, n in polys[::-1]:\n",
" # clip at y=yy and take (x, z) coordinate\n",
" verts = poly.slicePlane((0, yy, 0), (0, 1, 0))[:,[0,2]]\n",
" p2 = Polygon(verts)\n",
" path = p2.path # path to plot\n",
" if path is None: continue # skip if no intersection\n",
" # plot hitbox area\n",
" patch = patches.PathPatch(path, facecolor='#88ff88e8', lw=1)\n",
" ax.add_patch(patch)\n",
" ## update x, y range\n",
" xyMax = array([xyMax, *verts]).max(axis=0)\n",
" xyMin = array([xyMin, *verts]).min(axis=0)\n",
" # plot arrow (TODO: can be improved)\n",
" ## center of the clipped polygon\n",
" vc = np.mean(verts, axis=0)\n",
" ## normal of the wall\n",
" n2 = normalize(n[[0,2]])\n",
" ## direction vector (perpendicular to normal vector)\n",
" l2 = np.array([-n2[1], n2[0]])\n",
" loff = np.matmul(verts-vc, [n2[1], -n2[0]])\n",
" loffp = loff[loff>0].min() if len(loff[loff>0]) else 0\n",
" loffm = loff[loff<0].max() if len(loff[loff<0]) else 0\n",
" loffdis = loffp-loffm\n",
" arsize0 = 30 # arrow size (base)\n",
" arsize = (arsize0 if loffdis<=arsize0*5 else loffdis/5)*amul\n",
" if loffdis <= arsize0: continue # if too small\n",
" ## offset parallel to direction vector\n",
" for loff in np.linspace(loffm+arsize*0.64, loffp-arsize*0.64, int(loffdis//arsize)) if loffdis>=arsize*2 else [0]:\n",
" ## offset parallel to normal vector\n",
" for off, l in [(-40, 37.5), (5.0, 37.5)]:\n",
" plt.arrow(\n",
" *vc+loff*l2+n2*off*amul, *n2*l*amul,\n",
" width=arsize*0.27, head_width=arsize*0.72, head_length=15*amul,\n",
" length_includes_head=True, color='#080'\n",
" )\n",
"\n",
"# set x, y range of the figure\n",
"xMg, yMg = (xyMax-xyMin)*margin\n",
"xMax, yMax = xyMax\n",
"xMin, yMin = xyMin\n",
"ax.set_xlim(xMin-xMg, xMax+xMg)\n",
"ax.set_ylim(yMin-yMg, yMax+yMg)\n",
"\n",
"# if needed\n",
"ax.invert_xaxis()\n",
"#ax.invert_yaxis()\n",
"ax.grid()\n",
"\n",
"plt.title(f'y = %s (%s)'%(yy, 'airborne' if airborne else 'grounded'))\n",
"#fig.patch.set_facecolor('white')\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6de1a895-e624-4fe4-a6e6-bc8a820ca88f",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}