moved from h4570/tyra
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/audio.hpp"
|
||||
|
||||
#include <loadfile.h>
|
||||
#include "../include/utils/string.hpp"
|
||||
#include "../include/utils/debug.hpp"
|
||||
|
||||
#define SONG_NAME "MOV-CIRC.WAV"
|
||||
const int AUDSRV_BUFFER_SIZE = 1024 * 4;
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
Audio *audioRef;
|
||||
|
||||
Audio::Audio()
|
||||
{
|
||||
addedListeners = 0;
|
||||
isInitialized = 0;
|
||||
songLoaded = 0;
|
||||
isVolumeSet = 0;
|
||||
shouldPlay = 0;
|
||||
audioRef = this;
|
||||
initSema();
|
||||
loadModules();
|
||||
initAUDSRV();
|
||||
}
|
||||
|
||||
Audio::~Audio() {}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
/** Initialize audio module
|
||||
*
|
||||
* - Initialize threading semaphore
|
||||
* - Load modules
|
||||
* - Initialize AUDSRV
|
||||
* - Load background song
|
||||
*/
|
||||
void Audio::init(u32 t_listenersAmount)
|
||||
{
|
||||
PRINT_LOG("Initialize audio module started");
|
||||
listenersAmount = t_listenersAmount;
|
||||
listeners = new AudioListener *[t_listenersAmount];
|
||||
isInitialized = true;
|
||||
PRINT_LOG("Audio module initialized!");
|
||||
}
|
||||
|
||||
void Audio::addListener(AudioListener *t_listener)
|
||||
{
|
||||
listeners[addedListeners++] = t_listener;
|
||||
}
|
||||
|
||||
/** Initialize threading semaphore */
|
||||
void Audio::initSema()
|
||||
{
|
||||
PRINT_LOG("Creating semaphore started");
|
||||
sema.init_count = 0;
|
||||
sema.max_count = 1;
|
||||
sema.option = 0;
|
||||
fillbufferSema = CreateSema(&sema);
|
||||
PRINT_LOG("Semaphore created");
|
||||
}
|
||||
|
||||
/** Load LIBSD and AUDSRV */
|
||||
void Audio::loadModules()
|
||||
{
|
||||
PRINT_LOG("Modules loading started (LIBSD, AUDSRV)");
|
||||
ret = SifLoadModule("rom0:LIBSD", 0, NULL);
|
||||
ret = SifLoadModule("host:AUDSRV.IRX", 0, NULL);
|
||||
PRINT_LOG("Modules loaded");
|
||||
}
|
||||
|
||||
/** Initialize AUDSRV and install fillbuffer callback */
|
||||
void Audio::initAUDSRV()
|
||||
{
|
||||
PRINT_LOG("Initialize AUDSRV started");
|
||||
ret = audsrv_init();
|
||||
if (ret != 0)
|
||||
{
|
||||
PRINT_ERR("Failed to initialize AUDSRV!");
|
||||
printf("AUDSRV returned error string: %s", audsrv_get_error_string());
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = audsrv_on_fillbuf(AUDSRV_BUFFER_SIZE, Audio::fillbuffer, (void *)fillbufferSema);
|
||||
PRINT_LOG("AUDSRV initialized!");
|
||||
}
|
||||
}
|
||||
|
||||
/** Set audio format, volume and load song */
|
||||
void Audio::loadSong(char *t_filename)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
PRINT_ERR("Please initialize audio class first!");
|
||||
return;
|
||||
}
|
||||
PRINT_LOG("Song loading started");
|
||||
format.bits = 16;
|
||||
format.freq = 22050;
|
||||
format.channels = 2;
|
||||
char *fullFilename = String::createConcatenated("host:", t_filename);
|
||||
int err = audsrv_set_format(&format);
|
||||
if (err == 0)
|
||||
{
|
||||
PRINT_LOG("Audio format set");
|
||||
if (!isVolumeSet)
|
||||
setVolume(MAX_VOLUME);
|
||||
PRINT_LOG("Opening song file: " SONG_NAME);
|
||||
wav = fopen(fullFilename, "rb");
|
||||
delete[] fullFilename;
|
||||
if (wav == NULL)
|
||||
{
|
||||
PRINT_ERR("Failed to open wav file!");
|
||||
audsrv_quit();
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(wav, 0x30, SEEK_SET);
|
||||
played = 0;
|
||||
songLoaded = 1;
|
||||
PRINT_LOG("Song loaded!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PRINT_ERR("Failed to set audio format!");
|
||||
printf("AUDSRV returned error string: %s", audsrv_get_error_string());
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::setVolume(u8 t_volume)
|
||||
{
|
||||
audsrv_set_volume(t_volume);
|
||||
isVolumeSet = true;
|
||||
}
|
||||
|
||||
/** Create and start audio thread. */
|
||||
void Audio::startThread()
|
||||
{
|
||||
PRINT_LOG("Creating audio thread");
|
||||
extern void *_gp;
|
||||
audioThreadAttr.func = (void *)Audio::audioThread;
|
||||
audioThreadAttr.stack = audioThreadStack;
|
||||
audioThreadAttr.stack_size = STACK_SIZE;
|
||||
audioThreadAttr.gp_reg = (void *)&_gp;
|
||||
audioThreadAttr.initial_priority = 0x17;
|
||||
if ((audioThreadId = CreateThread(&audioThreadAttr)) < 0)
|
||||
PRINT_ERR("Create audio thread failed!");
|
||||
PRINT_LOG("Audio thread created");
|
||||
StartThread(audioThreadId, NULL);
|
||||
PRINT_LOG("Audio thread started");
|
||||
}
|
||||
|
||||
/** Do not call this function.
|
||||
* This is a audio thread, runned by AudioThread::start()
|
||||
*/
|
||||
void Audio::audioThread()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (audioRef->shouldPlay)
|
||||
audioRef->work();
|
||||
}
|
||||
}
|
||||
|
||||
void Audio::play()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
PRINT_ERR("Please initialize audio class first!");
|
||||
return;
|
||||
}
|
||||
shouldPlay = 1;
|
||||
}
|
||||
|
||||
void Audio::stop()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
PRINT_ERR("Please initialize audio class first!");
|
||||
return;
|
||||
}
|
||||
shouldPlay = 0;
|
||||
}
|
||||
|
||||
/** Play song and run it again on finish */
|
||||
void Audio::work()
|
||||
{
|
||||
if (!isTrackDone)
|
||||
{
|
||||
ret = fread(chunk, 1, sizeof(chunk), wav);
|
||||
if (ret > 0)
|
||||
{
|
||||
WaitSema(fillbufferSema);
|
||||
audsrv_play_audio(chunk, ret);
|
||||
}
|
||||
if (ret < (int)sizeof(chunk))
|
||||
{
|
||||
isTrackDone = true;
|
||||
return;
|
||||
}
|
||||
played++;
|
||||
if ((played + 20) % 41 == 0 && addedListeners)
|
||||
for (u32 i = 0; i < addedListeners; i++)
|
||||
listeners[i]->onAudioTick();
|
||||
}
|
||||
else
|
||||
{
|
||||
PRINT_LOG("Song " SONG_NAME " finished. Running again...");
|
||||
played = 0;
|
||||
fseek(wav, 0x30, SEEK_SET);
|
||||
isTrackDone = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Unload audio module by closing file stream and stopping AUDSRV */
|
||||
void Audio::unloadSong()
|
||||
{
|
||||
PRINT_LOG("Unloading audio module started");
|
||||
fclose(wav);
|
||||
PRINT_LOG("Song file stream closed");
|
||||
audsrv_quit();
|
||||
PRINT_LOG("AUDSRV stopped");
|
||||
PRINT_LOG("Audio module unloaded");
|
||||
}
|
||||
|
||||
/** Do not call this function.
|
||||
* This is a static callback function
|
||||
* for AUDSRV fillbuffer (semaphore)
|
||||
*/
|
||||
int Audio::fillbuffer(void *arg)
|
||||
{
|
||||
iSignalSema((int)arg);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/camera_base.hpp"
|
||||
|
||||
#include <fastmath.h>
|
||||
#include "../include/utils/debug.hpp"
|
||||
#include "../include/utils/math.hpp"
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
/** Initializes vars and calculate width/height of near and far plane
|
||||
* @param fov (FOV in radians)/2
|
||||
* @param ratio Aspect ratio
|
||||
*/
|
||||
CameraBase::CameraBase(ScreenSettings *t_screen, Vector3 *t_position, Vector3 *t_up, Vector3 *t_unitCirclePosition)
|
||||
: screen(t_screen)
|
||||
{
|
||||
PRINT_LOG("Initializing frustum");
|
||||
farPlaneDist = screen->farPlaneDist;
|
||||
nearPlaneDist = screen->nearPlaneDist;
|
||||
float tang = tanf(screen->fov * Math::HALF_ANG2RAD);
|
||||
nearHeight = tang * nearPlaneDist;
|
||||
nearWidth = nearHeight * screen->aspectRatio;
|
||||
farHeight = tang * farPlaneDist;
|
||||
farWidth = farHeight * screen->aspectRatio;
|
||||
position2 = t_position;
|
||||
up2 = t_up;
|
||||
unitCirclePosition2 = t_unitCirclePosition;
|
||||
PRINT_LOG("CameraBase initialized!");
|
||||
}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
/** Calculates and updates frustum planes
|
||||
* http://www.lighthouse3d.com/tutorials/view-frustum-culling/geometric-approach-extracting-the-planes/
|
||||
*/
|
||||
void CameraBase::updatePlanes(Vector3 t_target)
|
||||
{
|
||||
Vector3 nearCenter, farCenter, X, Y, Z;
|
||||
// compute the Z axis of camera
|
||||
Z = *position2 - t_target;
|
||||
Z.normalize();
|
||||
// X axis of camera of given "up" vector and Z axis
|
||||
X = *up2 * Z;
|
||||
X.normalize();
|
||||
// the real "up" vector is the cross product of Z and X
|
||||
Y = Z * X;
|
||||
// compute the center of the near and far planes
|
||||
nearCenter = *position2 - Z * nearPlaneDist;
|
||||
farCenter = *position2 - Z * farPlaneDist;
|
||||
// compute the 8 corners of the frustum
|
||||
ntl = nearCenter + Y * nearHeight - X * nearWidth;
|
||||
ntr = nearCenter + Y * nearHeight + X * nearWidth;
|
||||
nbl = nearCenter - Y * nearHeight - X * nearWidth;
|
||||
nbr = nearCenter - Y * nearHeight + X * nearWidth;
|
||||
|
||||
ftl = farCenter + Y * farHeight - X * farWidth;
|
||||
fbr = farCenter - Y * farHeight + X * farWidth;
|
||||
ftr = farCenter + Y * farHeight + X * farWidth;
|
||||
fbl = farCenter - Y * farHeight - X * farWidth;
|
||||
|
||||
planes[0].update(ntr, ntl, ftl); // Top
|
||||
planes[1].update(nbl, nbr, fbr); // BOTTOM
|
||||
planes[2].update(ntl, nbl, fbl); // LEFT
|
||||
planes[3].update(nbr, ntr, fbr); // RIGHT
|
||||
planes[4].update(ntl, ntr, nbr); // NEAR
|
||||
planes[5].update(ftr, ftl, fbl); // FAR
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/gif_sender.hpp"
|
||||
|
||||
#include "../include/utils/math.hpp"
|
||||
#include "../include/utils/debug.hpp"
|
||||
#include "../include/modules/light.hpp"
|
||||
#include <dma.h>
|
||||
#include <draw.h>
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <dma_tags.h>
|
||||
#include <gs_psm.h>
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
/** Initializes vars and creates data transfer packets
|
||||
* @param packetSize Size of data packet, should be increased when more data will be rendered
|
||||
*/
|
||||
GifSender::GifSender(u32 t_packetSize, ScreenSettings *t_screen) : screen(t_screen)
|
||||
{
|
||||
PRINT_LOG("Initializing GifSender");
|
||||
packetSize = t_packetSize;
|
||||
packets[0] = packet_init(t_packetSize, PACKET_NORMAL);
|
||||
packets[1] = packet_init(t_packetSize, PACKET_NORMAL);
|
||||
PRINT_LOG("GifSender initialized!");
|
||||
}
|
||||
|
||||
/** Releases packets memory */
|
||||
GifSender::~GifSender()
|
||||
{
|
||||
packet_free(packets[0]);
|
||||
packet_free(packets[1]);
|
||||
}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
#include <gif_tags.h>
|
||||
#include <gs_gp.h>
|
||||
|
||||
/** Send texture via GIF */
|
||||
void GifSender::sendTexture(Texture &texture, texbuffer_t *t_texBuffer)
|
||||
{
|
||||
const u16 packetSize = 40;
|
||||
packet_t *packet = packet_init(packetSize, PACKET_NORMAL);
|
||||
qword_t *q = packet->data;
|
||||
q = draw_texture_transfer(q, texture.data, texture.width, texture.height, GS_PSM_24, t_texBuffer->address, t_texBuffer->width);
|
||||
DMATAG_CNT(q, 2, 0, 0, 0);
|
||||
q++;
|
||||
q = draw_texture_wrapping(q, 0, &texture.wrapSettings);
|
||||
q = draw_texture_flush(q);
|
||||
dma_channel_send_chain(DMA_CHANNEL_GIF, packet->data, q - packet->data, 0, 0);
|
||||
dma_wait_fast();
|
||||
packet_free(packet);
|
||||
}
|
||||
|
||||
void GifSender::sendClear(zbuffer_t *t_zBuffer)
|
||||
{
|
||||
packet_t *packet = packet_init(100, PACKET_NORMAL);
|
||||
qword_t *q = packet->data;
|
||||
q++;
|
||||
q = draw_disable_tests(q, 0, t_zBuffer);
|
||||
q = draw_clear(q, 0,
|
||||
2048.0F - (screen->width / 2), 2048.0F - (screen->height / 2),
|
||||
screen->width, screen->height,
|
||||
0x10, 0x10, 0x10);
|
||||
q = draw_enable_tests(q, 0, t_zBuffer);
|
||||
q = draw_finish(q);
|
||||
DMATAG_END(packet->data, q - packet->data - 1, 0, 0, 0);
|
||||
dma_channel_send_chain(DMA_CHANNEL_GIF, packet->data, q - packet->data, 0, 0);
|
||||
dma_wait_fast();
|
||||
packet_free(packet);
|
||||
}
|
||||
|
||||
/** Used in game loop.
|
||||
* Switches current packet to next one
|
||||
*/
|
||||
void GifSender::initPacket(u8 t_context)
|
||||
{
|
||||
currentPacket = packets[t_context];
|
||||
if (currentPacket->qwords > (u32)packetSize || currentPacket->qwords < 0)
|
||||
PRINT_ERR("GifSender packet size error. Please consider to change packet size!\n");
|
||||
q = currentPacket->data;
|
||||
dmatag = q;
|
||||
q++;
|
||||
isAnyObjectAdded = 0;
|
||||
}
|
||||
|
||||
/** Sends packet to GIF. */
|
||||
void GifSender::sendPacket()
|
||||
{
|
||||
if (isAnyObjectAdded)
|
||||
{
|
||||
dmatag = q;
|
||||
q++;
|
||||
}
|
||||
q = draw_finish(q);
|
||||
DMATAG_END(dmatag, q - dmatag - 1, 0, 0, 0);
|
||||
dma_wait_fast();
|
||||
dma_channel_send_chain(DMA_CHANNEL_GIF, currentPacket->data, q - currentPacket->data, 0, 0);
|
||||
}
|
||||
|
||||
/** Adds clear screen to current packet */
|
||||
void GifSender::addClear(zbuffer_t *t_zBuffer)
|
||||
{
|
||||
q = draw_disable_tests(q, 0, t_zBuffer);
|
||||
q = draw_clear(q, 0,
|
||||
2048.0F - (screen->width / 2), 2048.0F - (screen->height / 2),
|
||||
screen->width, screen->height,
|
||||
0x10, 0x10, 0x10);
|
||||
q = draw_enable_tests(q, 0, t_zBuffer);
|
||||
}
|
||||
|
||||
/** Adds 3D objects to current packet
|
||||
* @param worldView Matrix
|
||||
* @param perspective Matrix with .setPerpsective() used [clone of gluPerspective]
|
||||
* @param objects3D Array of 3D objects pointers
|
||||
* @param amount Amount of 3D objects
|
||||
*/
|
||||
void GifSender::addObjects(RenderData *t_renderData, Mesh **t_objects3D, u32 t_amount, LightBulb *t_bulbs, u16 t_bulbsCount)
|
||||
{
|
||||
if (!isAnyObjectAdded)
|
||||
{
|
||||
isAnyObjectAdded = true;
|
||||
DMATAG_CNT(dmatag, q - dmatag - 1, 0, 0, 0); // init tag (before first 3d obj)
|
||||
}
|
||||
qword_t *tempDMATag;
|
||||
tempDMATag = q;
|
||||
q++;
|
||||
|
||||
q = draw_texture_sampling(q, 0, &t_objects3D[0]->spec->lod);
|
||||
q = draw_texturebuffer(q, 0, &t_objects3D[0]->spec->textureBuffer, &t_objects3D[0]->spec->clut);
|
||||
dw = (u64 *)draw_prim_start(q, 0, t_renderData->prim, &t_objects3D[0]->color);
|
||||
|
||||
for (u32 i = 0; i < t_amount; i++)
|
||||
if (t_objects3D[i]->shouldBeFrustumCulled == 0 || t_objects3D[i]->isInFrustum(t_renderData->frustumPlanes))
|
||||
{
|
||||
u32 vertexCount = calc3DObject(*t_renderData->perspective, *t_objects3D[i], t_renderData, t_bulbs, t_bulbsCount);
|
||||
addCurrentCalcs(vertexCount);
|
||||
delete[] xyz;
|
||||
delete[] rgbaq;
|
||||
delete[] st;
|
||||
}
|
||||
|
||||
if ((u32)dw % 16) // if we are in the middle of qw, switch packet
|
||||
*dw++ = 0;
|
||||
|
||||
q = draw_prim_end((qword_t *)dw, 3, DRAW_STQ_REGLIST);
|
||||
|
||||
DMATAG_CNT(tempDMATag, q - tempDMATag - 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
/** Calculates 3D object data into xyz, rgbq, st
|
||||
* After it addCurrentSTQ() can be done
|
||||
* @param worldView Matrix
|
||||
* @param perspective Matrix with .setPerpsective() used [clone of gluPerspective]
|
||||
* @param mesh 3D object
|
||||
* @returns Vertex count
|
||||
*/
|
||||
u32 GifSender::calc3DObject(Matrix t_perspective, Mesh &t_mesh, RenderData *t_renderData, LightBulb *t_bulbs, u16 t_bulbsCount)
|
||||
{
|
||||
u32 vertexCount = t_mesh.getVertexCount();
|
||||
|
||||
VECTOR *vertices = new VECTOR[vertexCount];
|
||||
VECTOR *normals = new VECTOR[vertexCount];
|
||||
VECTOR *coordinates = new VECTOR[vertexCount];
|
||||
VECTOR *colors = new VECTOR[vertexCount];
|
||||
vertexCount = t_mesh.getDrawData(0, vertices, normals, coordinates, colors, *t_renderData->cameraPosition);
|
||||
|
||||
xyz = new xyz_t[vertexCount];
|
||||
rgbaq = new color_t[vertexCount];
|
||||
st = new texel_t[vertexCount];
|
||||
|
||||
VECTOR position, rotation;
|
||||
|
||||
vec3ToNative(position, t_mesh.position, 1.0F);
|
||||
vec3ToNative(rotation, t_mesh.rotation, 1.0F);
|
||||
|
||||
create_local_world(localWorld, position, rotation);
|
||||
|
||||
const u8 SHOULD_BE_LIGHTED = t_bulbs != NULL && t_mesh.shouldBeLighted;
|
||||
|
||||
if (SHOULD_BE_LIGHTED)
|
||||
create_local_light(localLight, rotation);
|
||||
|
||||
// I cant put perspective from renderData here. PS2SDK bug?
|
||||
create_local_screen(localScreen, localWorld, t_renderData->worldView->data, t_perspective.data);
|
||||
|
||||
if (SHOULD_BE_LIGHTED)
|
||||
{
|
||||
const u16 lightsCount = Light::getLightsCount(t_bulbsCount);
|
||||
VECTOR *lightDirections = new VECTOR[lightsCount];
|
||||
VECTOR *lightColors = new VECTOR[lightsCount];
|
||||
int *lightTypes = new int[lightsCount];
|
||||
|
||||
VECTOR *lights = new VECTOR[vertexCount];
|
||||
calculate_normals(normals, vertexCount, normals, localLight);
|
||||
|
||||
Light::calculateLight(lightDirections, lightColors, lightTypes, t_bulbs, t_bulbsCount, t_mesh.position);
|
||||
|
||||
calculate_lights(lights, vertexCount, normals, lightDirections, lightColors, lightTypes, lightsCount);
|
||||
|
||||
calculate_colours(colors, vertexCount, colors, lights);
|
||||
|
||||
delete[] lightDirections;
|
||||
delete[] lightColors;
|
||||
delete[] lightTypes;
|
||||
delete[] lights;
|
||||
}
|
||||
|
||||
calculate_vertices(vertices, vertexCount, vertices, localScreen);
|
||||
|
||||
convertCalcs(vertexCount, vertices, colors, coordinates, t_mesh.color.a);
|
||||
|
||||
delete[] vertices;
|
||||
delete[] normals;
|
||||
delete[] coordinates;
|
||||
delete[] colors;
|
||||
|
||||
return vertexCount;
|
||||
}
|
||||
|
||||
void GifSender::convertCalcs(u32 t_vertexCount, VECTOR *t_vertices, VECTOR *t_colors, VECTOR *t_sts, u8 t_alpha)
|
||||
{
|
||||
// TODO get this via screensettings
|
||||
const s32 centerX = ftoi4(2048);
|
||||
const s32 centerY = ftoi4(2048);
|
||||
const u32 maxZ = ftoi4(((float)0xFFFFFF) / 32.0F);
|
||||
float q = 1.00F;
|
||||
for (u32 i = 0; i < t_vertexCount; i++)
|
||||
{
|
||||
xyz[i].x = (u16)((t_vertices[i][0] + 1.0F) * centerX);
|
||||
xyz[i].y = (u16)((t_vertices[i][1] + 1.0F) * centerY);
|
||||
xyz[i].z = (u32)((t_vertices[i][2] + 1.0F) * maxZ);
|
||||
|
||||
if (t_vertices[i][3])
|
||||
q = 1 / t_vertices[i][3];
|
||||
|
||||
st[i].s = t_sts[i][0] * q;
|
||||
st[i].t = t_sts[i][1] * q;
|
||||
|
||||
rgbaq[i].r = (u8)(t_colors[i][0] * 128.0F);
|
||||
rgbaq[i].g = (u8)(t_colors[i][1] * 128.0F);
|
||||
rgbaq[i].b = (u8)(t_colors[i][2] * 128.0F);
|
||||
rgbaq[i].a = t_alpha;
|
||||
rgbaq[i].q = q;
|
||||
}
|
||||
}
|
||||
|
||||
/** Update q's double words. It may look strange, but this workaround
|
||||
* use's a 64-bit pointer to simplify adding data to the packet.
|
||||
*/
|
||||
void GifSender::addCurrentCalcs(u32 &t_vertexCount)
|
||||
{
|
||||
for (u32 i = 0; i < t_vertexCount; i++)
|
||||
{
|
||||
*dw++ = rgbaq[i].rgbaq;
|
||||
*dw++ = st[i].uv;
|
||||
*dw++ = xyz[i].xyz;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/light.hpp"
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
Light::Light() {}
|
||||
|
||||
Light::~Light() {}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
const u8 ADDITIONAL_LIGHTS = 1; // Ambient
|
||||
|
||||
u16 Light::getLightsCount(u32 t_bulbsCount) { return t_bulbsCount + ADDITIONAL_LIGHTS; }
|
||||
|
||||
/** Calculates lighting. Used in gifSender in object 3D calculations */
|
||||
void Light::calculateLight(VECTOR *t_lightDirections, VECTOR *t_lightColors, int *t_lightTypes, LightBulb *t_bulbs, u32 t_bulbsCount, Vector3 t_objPosition)
|
||||
{
|
||||
// --- Ambient light
|
||||
|
||||
t_lightTypes[0] = LIGHT_AMBIENT;
|
||||
|
||||
t_lightDirections[0][0] = 0.0F;
|
||||
t_lightDirections[0][1] = 0.0F;
|
||||
t_lightDirections[0][2] = 0.0F;
|
||||
t_lightDirections[0][3] = 1.0F;
|
||||
|
||||
t_lightColors[0][0] = 0.0F;
|
||||
t_lightColors[0][1] = 0.0F;
|
||||
t_lightColors[0][2] = 0.0F;
|
||||
t_lightColors[0][3] = 1.0F;
|
||||
|
||||
// ---
|
||||
|
||||
for (u8 i = 0; i < t_bulbsCount; i++)
|
||||
{
|
||||
t_lightTypes[i + 1] = LIGHT_DIRECTIONAL;
|
||||
|
||||
Vector3 newLight = Vector3(t_objPosition.x - t_bulbs[i].position.x,
|
||||
t_objPosition.y - t_bulbs[i].position.y,
|
||||
t_objPosition.z - t_bulbs[i].position.z);
|
||||
|
||||
newLight.normalize();
|
||||
newLight = newLight *
|
||||
(.1F +
|
||||
(t_bulbs[i].intensity / t_bulbs[i].position.distanceTo(t_objPosition)));
|
||||
|
||||
t_lightDirections[i + 1][0] = newLight.x;
|
||||
t_lightDirections[i + 1][1] = newLight.y;
|
||||
t_lightDirections[i + 1][2] = newLight.z;
|
||||
t_lightDirections[i + 1][3] = 1.0F;
|
||||
|
||||
t_lightColors[i + 1][0] = 0.8F;
|
||||
t_lightColors[i + 1][1] = 0.8F;
|
||||
t_lightColors[i + 1][2] = 0.8F;
|
||||
t_lightColors[i + 1][3] = 1.0F;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/pad.hpp"
|
||||
|
||||
#include "../include/utils/debug.hpp"
|
||||
#include <kernel.h>
|
||||
#include <loadfile.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
/** Init vars, load modules, opens pad port and initializes pad */
|
||||
Pad::Pad()
|
||||
{
|
||||
this->oldPad = 0;
|
||||
this->loadModules();
|
||||
padInit(0);
|
||||
this->port = 0; // 0 -> Connector 1, 1 -> Connector 2
|
||||
this->slot = 0; // Always zero if not using multitap
|
||||
if ((this->ret = padPortOpen(this->port, this->slot, padBuf)) == 0)
|
||||
{
|
||||
PRINT_ERR("padPortOpen failed!");
|
||||
printf("padPortOpen returned: %d\n", this->ret);
|
||||
SleepThread();
|
||||
}
|
||||
if (!this->initPad())
|
||||
{
|
||||
PRINT_ERR("initPad failed!");
|
||||
SleepThread();
|
||||
}
|
||||
}
|
||||
|
||||
Pad::~Pad() {}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
/** Load SIO2MAN and PADMAN modules */
|
||||
void Pad::loadModules()
|
||||
{
|
||||
PRINT_LOG("Loading pad modules");
|
||||
this->ret = SifLoadModule("rom0:SIO2MAN", 0, NULL);
|
||||
if (this->ret < 0)
|
||||
{
|
||||
PRINT_ERR("SifLoadModule (SIO2MAN) failed!");
|
||||
printf("SifLoadModule returned: %d\n", this->ret);
|
||||
SleepThread();
|
||||
}
|
||||
this->ret = SifLoadModule("rom0:PADMAN", 0, NULL);
|
||||
if (this->ret < 0)
|
||||
{
|
||||
PRINT_ERR("SifLoadModule (PADMAN) failed!");
|
||||
printf("SifLoadModule returned: %d\n", this->ret);
|
||||
SleepThread();
|
||||
}
|
||||
PRINT_LOG("Pad modules loaded!");
|
||||
}
|
||||
|
||||
/** Wait when pad will be ready (stable and ready) */
|
||||
int Pad::waitPadReady()
|
||||
{
|
||||
int state;
|
||||
int lastState;
|
||||
char stateString[16];
|
||||
state = padGetState(this->port, this->slot);
|
||||
lastState = -1;
|
||||
while ((state != PAD_STATE_STABLE) && (state != PAD_STATE_FINDCTP1))
|
||||
{
|
||||
if (state != lastState)
|
||||
{
|
||||
padStateInt2String(state, stateString);
|
||||
PRINT_LOG("Pad state changed");
|
||||
printf("Curent pad(%d,%d) status: %s\n", this->port, this->slot, stateString);
|
||||
}
|
||||
lastState = state;
|
||||
state = padGetState(this->port, this->slot);
|
||||
}
|
||||
// Were the pad ever 'out of sync'?
|
||||
if (lastState != -1)
|
||||
PRINT_LOG("Pad is ready!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Initializes and checks type of pad */
|
||||
int Pad::initPad()
|
||||
{
|
||||
PRINT_LOG("Initializing pad");
|
||||
this->waitPadReady();
|
||||
// How many different modes can this device operate in?
|
||||
// i.e. get # entrys in the modetable
|
||||
int modes = padInfoMode(this->port, this->slot, PAD_MODETABLE, -1);
|
||||
if (modes == 0)
|
||||
{
|
||||
PRINT_ERR("Connected device is not a dual shock controller!"); // (it has no actuator engines)
|
||||
return 1;
|
||||
}
|
||||
// Verify that the controller has a DUAL SHOCK mode
|
||||
int i = 0;
|
||||
do
|
||||
{
|
||||
if (padInfoMode(this->port, this->slot, PAD_MODETABLE, i) == PAD_TYPE_DUALSHOCK)
|
||||
break;
|
||||
i++;
|
||||
} while (i < modes);
|
||||
|
||||
if (i >= modes)
|
||||
{
|
||||
PRINT_ERR("Connected device is not a dual shock controller!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If ExId != 0x0 => This controller has actuator engines
|
||||
// This check should always pass if the Dual Shock test above passed
|
||||
this->ret = padInfoMode(this->port, this->slot, PAD_MODECUREXID, 0);
|
||||
if (this->ret == 0)
|
||||
{
|
||||
PRINT_ERR("Connected device is not a dual shock controller!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
PRINT_LOG("Enabling dual shock functions.");
|
||||
|
||||
// When using MMODE_LOCK, user cant change mode with Select button
|
||||
padSetMainMode(this->port, this->slot, PAD_MMODE_DUALSHOCK, PAD_MMODE_LOCK);
|
||||
|
||||
this->waitPadReady();
|
||||
printf("Pad has pressure sensitive buttons? %d\n", padInfoPressMode(this->port, this->slot));
|
||||
this->waitPadReady();
|
||||
padEnterPressMode(this->port, this->slot); // Set pressure sensitive mode
|
||||
this->waitPadReady();
|
||||
this->actuators = padInfoAct(this->port, this->slot, -1, 0);
|
||||
printf("# of actuators: %d\n", this->actuators);
|
||||
if (actuators != 0)
|
||||
{
|
||||
this->actAlign[0] = 0; // Enable small engine
|
||||
this->actAlign[1] = 1; // Enable big engine
|
||||
this->actAlign[2] = 0xff;
|
||||
this->actAlign[3] = 0xff;
|
||||
this->actAlign[4] = 0xff;
|
||||
this->actAlign[5] = 0xff;
|
||||
this->waitPadReady();
|
||||
printf("padSetActAlign: %d\n", padSetActAlign(this->port, this->slot, actAlign));
|
||||
}
|
||||
else
|
||||
printf("Did not find any actuators.\n");
|
||||
this->waitPadReady();
|
||||
PRINT_LOG("Pad initialized!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Updates state of joys/buttons */
|
||||
void Pad::update()
|
||||
{
|
||||
int x = 0;
|
||||
this->ret = padGetState(this->port, this->slot);
|
||||
while ((this->ret != PAD_STATE_STABLE) && (this->ret != PAD_STATE_FINDCTP1))
|
||||
{
|
||||
if (this->ret == PAD_STATE_DISCONN)
|
||||
printf("Pad(%d, %d) is disconnected\n", this->port, this->slot);
|
||||
this->ret = padGetState(this->port, this->slot);
|
||||
}
|
||||
if (x == 1)
|
||||
printf("Pad: OK!\n");
|
||||
|
||||
this->ret = padRead(this->port, this->slot, &this->buttons); // this->port, this->slot, this->buttons
|
||||
|
||||
if (this->ret != 0)
|
||||
{
|
||||
this->padData = 0xffff ^ this->buttons.btns;
|
||||
|
||||
this->newPad = this->padData & ~this->oldPad;
|
||||
this->oldPad = this->padData;
|
||||
this->reset();
|
||||
this->rJoyH = this->buttons.rjoy_h;
|
||||
this->rJoyV = this->buttons.rjoy_v;
|
||||
this->lJoyH = this->buttons.ljoy_h;
|
||||
this->lJoyV = this->buttons.ljoy_v;
|
||||
|
||||
if (this->newPad & PAD_CROSS)
|
||||
this->isCrossClicked = 1;
|
||||
if (this->newPad & PAD_SQUARE)
|
||||
this->isSquareClicked = 1;
|
||||
if (this->newPad & PAD_TRIANGLE)
|
||||
this->isTriangleClicked = 1;
|
||||
if (this->newPad & PAD_CIRCLE)
|
||||
this->isCircleClicked = 1;
|
||||
|
||||
if (this->buttons.up_p > 0)
|
||||
this->isDpadUpPressed = 1;
|
||||
if (this->buttons.down_p > 0)
|
||||
this->isDpadDownPressed = 1;
|
||||
if (this->buttons.left_p)
|
||||
this->isDpadLeftPressed = 1;
|
||||
if (this->buttons.right_p)
|
||||
this->isDpadRightPressed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets state of joys/buttons */
|
||||
void Pad::reset()
|
||||
{
|
||||
this->isCrossClicked = 0;
|
||||
this->isSquareClicked = 0;
|
||||
this->isTriangleClicked = 0;
|
||||
this->isCircleClicked = 0;
|
||||
this->isDpadUpPressed = 0;
|
||||
this->isDpadDownPressed = 0;
|
||||
this->isDpadLeftPressed = 0;
|
||||
this->isDpadRightPressed = 0;
|
||||
this->lJoyH = 0;
|
||||
this->lJoyV = 0;
|
||||
this->rJoyH = 0;
|
||||
this->rJoyV = 0;
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/renderer.hpp"
|
||||
|
||||
#include <dma.h>
|
||||
#include <graph.h>
|
||||
#include <packet.h>
|
||||
#include <draw.h>
|
||||
#include <gs_psm.h>
|
||||
#include "../include/utils/debug.hpp"
|
||||
#include "../include/utils/math.hpp"
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
/** Initialize DMA<->GIF channel
|
||||
* Allocate buffers
|
||||
* Initialize screen
|
||||
* Initialize drawing environment
|
||||
* Load/setup textures
|
||||
* @param screenW Half of screen width
|
||||
* @param screenH Half of screen height
|
||||
*/
|
||||
Renderer::Renderer(u32 t_packetSize, ScreenSettings *t_screen)
|
||||
{
|
||||
PRINT_LOG("Initializing renderer");
|
||||
dma_channel_initialize(DMA_CHANNEL_GIF, NULL, 0); // Initialize DMA to enable data transfer
|
||||
dma_channel_fast_waits(DMA_CHANNEL_GIF);
|
||||
context = 0;
|
||||
lastTextureId = 0;
|
||||
isFrameEmpty = false;
|
||||
flipPacket = packet_init(3, PACKET_UCAB); // Uncached accelerated
|
||||
allocateBuffers(t_screen->width, t_screen->height);
|
||||
initDrawingEnv(t_screen->width, t_screen->height);
|
||||
setPrim();
|
||||
gifSender = new GifSender(t_packetSize, t_screen);
|
||||
vifSender = new VifSender();
|
||||
perspective.setPerspective(*t_screen);
|
||||
renderData.perspective = &perspective;
|
||||
PRINT_LOG("Renderer initialized!");
|
||||
}
|
||||
|
||||
Renderer::~Renderer() {}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
/** Initializes drawing environment (1st app packet) */
|
||||
void Renderer::initDrawingEnv(float t_screenW, float t_screenH)
|
||||
{
|
||||
PRINT_LOG("Initializing drawing environment");
|
||||
packet_t *packet = packet_init(20, PACKET_NORMAL);
|
||||
u16 halfW = (u16)t_screenW / 2;
|
||||
u16 halfH = (u16)t_screenH / 2;
|
||||
qword_t *q = packet->data; // Generic qword pointer.
|
||||
q = draw_setup_environment(q, 0, frameBuffers, &(zBuffer));
|
||||
q = draw_primitive_xyoffset(q, 0, (2048 - halfW), (2048 - halfH));
|
||||
q = draw_finish(q);
|
||||
// Now send the packet, no need to wait since it's the first.
|
||||
dma_channel_send_normal(DMA_CHANNEL_GIF, packet->data, q - packet->data, 0, 0);
|
||||
dma_wait_fast();
|
||||
packet_free(packet);
|
||||
PRINT_LOG("Drawing environment initialized!");
|
||||
}
|
||||
|
||||
/** Sets drawing prim for all 3D objects */
|
||||
void Renderer::setPrim()
|
||||
{
|
||||
prim.type = PRIM_TRIANGLE;
|
||||
prim.shading = PRIM_SHADE_FLAT;
|
||||
prim.mapping = DRAW_ENABLE;
|
||||
prim.fogging = DRAW_DISABLE;
|
||||
prim.blending = DRAW_ENABLE;
|
||||
prim.antialiasing = DRAW_DISABLE;
|
||||
prim.mapping_type = PRIM_MAP_ST;
|
||||
prim.colorfix = PRIM_UNFIXED;
|
||||
renderData.prim = &prim;
|
||||
PRINT_LOG("Prim set!");
|
||||
}
|
||||
|
||||
void Renderer::changeTexture(Mesh *t_mesh, u8 t_textureIndex)
|
||||
{
|
||||
if (t_mesh->spec->textures[t_textureIndex].id != lastTextureId)
|
||||
{
|
||||
lastTextureId = t_mesh->spec->textures[t_textureIndex].id;
|
||||
t_mesh->spec->deallocateTextureBuffer();
|
||||
t_mesh->spec->allocateTextureBuffer(t_mesh->spec->textures[t_textureIndex].width, t_mesh->spec->textures[t_textureIndex].height);
|
||||
GifSender::sendTexture(t_mesh->spec->textures[t_textureIndex], &t_mesh->spec->textureBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
/** Defines and allocates framebuffers and zbuffer */
|
||||
void Renderer::allocateBuffers(float t_screenW, float t_screenH)
|
||||
{
|
||||
frameBuffers[0].width = (u16)t_screenW;
|
||||
frameBuffers[0].height = (u16)t_screenH;
|
||||
frameBuffers[0].mask = 0;
|
||||
frameBuffers[0].psm = GS_PSM_32;
|
||||
frameBuffers[0].address = graph_vram_allocate((u16)t_screenW, (u16)t_screenH, frameBuffers[0].psm, GRAPH_ALIGN_PAGE);
|
||||
|
||||
frameBuffers[1].width = (u16)t_screenW;
|
||||
frameBuffers[1].height = (u16)t_screenH;
|
||||
frameBuffers[1].mask = 0;
|
||||
frameBuffers[1].psm = GS_PSM_32;
|
||||
frameBuffers[1].address = graph_vram_allocate((u16)t_screenW, (u16)t_screenH, frameBuffers[1].psm, GRAPH_ALIGN_PAGE);
|
||||
|
||||
zBuffer.enable = DRAW_ENABLE;
|
||||
zBuffer.mask = 0;
|
||||
zBuffer.method = ZTEST_METHOD_GREATER_EQUAL;
|
||||
zBuffer.zsm = GS_ZBUF_32;
|
||||
zBuffer.address = graph_vram_allocate((u16)t_screenW, (u16)t_screenH, zBuffer.zsm, GRAPH_ALIGN_PAGE);
|
||||
PRINT_LOG("Framebuffers, zBuffer set and allocated!");
|
||||
|
||||
// Initialize the screen and tie the first framebuffer to the read circuits.
|
||||
graph_initialize(frameBuffers[1].address, frameBuffers[1].width, frameBuffers[1].height, frameBuffers[1].psm, 0, 0);
|
||||
}
|
||||
|
||||
/// --- Draw: PATH3
|
||||
|
||||
/** PATH3 Many + lighting */
|
||||
void Renderer::drawByPath3(Mesh **t_meshes, u16 t_amount, LightBulb *t_bulbs, u16 t_bulbsCount)
|
||||
{
|
||||
beginFrameIfNeeded();
|
||||
gifSender->initPacket(context);
|
||||
// TODO
|
||||
changeTexture(t_meshes[0], 0);
|
||||
gifSender->addObjects(&renderData, t_meshes, t_amount, t_bulbs, t_bulbsCount);
|
||||
gifSender->sendPacket();
|
||||
draw_wait_finish();
|
||||
}
|
||||
|
||||
/** PATH3 Single + lighting */
|
||||
void Renderer::drawByPath3(Mesh *t_mesh, LightBulb *t_bulbs, u16 t_bulbsCount)
|
||||
{
|
||||
beginFrameIfNeeded();
|
||||
gifSender->initPacket(context);
|
||||
// TODO
|
||||
changeTexture(t_mesh, 0);
|
||||
gifSender->addObjects(&renderData, &t_mesh, 1, t_bulbs, t_bulbsCount);
|
||||
gifSender->sendPacket();
|
||||
draw_wait_finish();
|
||||
}
|
||||
|
||||
/** PATH3 Many */
|
||||
void Renderer::drawByPath3(Mesh **t_meshes, u16 t_amount) { drawByPath3(t_meshes, t_amount, NULL, 0); }
|
||||
|
||||
/** PATH3 Single */
|
||||
void Renderer::drawByPath3(Mesh *t_mesh) { drawByPath3(t_mesh, NULL, 0); }
|
||||
|
||||
/// --- Draw: PATH1
|
||||
|
||||
/** PATH1 Many + lighting */
|
||||
void Renderer::draw(Mesh **t_meshes, u16 t_amount, LightBulb *t_bulbs, u16 t_bulbsCount)
|
||||
{
|
||||
// TODO
|
||||
beginFrameIfNeeded();
|
||||
for (u16 i = 0; i < t_amount; i++)
|
||||
draw(t_meshes[i], t_bulbs, t_bulbsCount);
|
||||
}
|
||||
|
||||
/** PATH1 Single + lighting */
|
||||
void Renderer::draw(Mesh *t_mesh, LightBulb *t_bulbs, u16 t_bulbsCount)
|
||||
{
|
||||
beginFrameIfNeeded();
|
||||
// TODO VU1 send single list here
|
||||
if (!t_mesh->isObjLoaded && !t_mesh->isDffLoaded && !t_mesh->isMd2Loaded)
|
||||
return;
|
||||
u32 vertCount = t_mesh->getVertexCount();
|
||||
VECTOR *vertices = new VECTOR[vertCount];
|
||||
VECTOR *normals = new VECTOR[vertCount];
|
||||
VECTOR *coordinates = new VECTOR[vertCount];
|
||||
VECTOR *colors = new VECTOR[vertCount];
|
||||
if (t_mesh->isObjLoaded || t_mesh->isMd2Loaded)
|
||||
{
|
||||
changeTexture(t_mesh, 0);
|
||||
vertCount = t_mesh->getDrawData(0, vertices, normals, coordinates, colors, *renderData.cameraPosition);
|
||||
vifSender->drawMesh(&renderData, perspective, vertCount, vertices, normals, coordinates, colors, t_mesh, t_bulbs, t_bulbsCount);
|
||||
}
|
||||
else if (t_mesh->isDffLoaded)
|
||||
for (u32 i = 0; i < t_mesh->dff->clump.geometryList.geometries[0].extension.materialSplit.header.splitCount; i++)
|
||||
{
|
||||
const u32 currentTexI = t_mesh->dff->clump.geometryList.geometries[0].extension.materialSplit.splitInformation[i].materialIndex;
|
||||
changeTexture(t_mesh, currentTexI);
|
||||
vertCount = t_mesh->getDrawData(i, vertices, normals, coordinates, colors, *renderData.cameraPosition);
|
||||
vifSender->drawMesh(&renderData, perspective, vertCount, vertices, normals, coordinates, colors, t_mesh, t_bulbs, t_bulbsCount);
|
||||
}
|
||||
delete[] vertices;
|
||||
delete[] normals;
|
||||
delete[] coordinates;
|
||||
delete[] colors;
|
||||
}
|
||||
|
||||
/** PATH1 Many */
|
||||
void Renderer::draw(Mesh **t_objects3D, u16 t_amount) { draw(t_objects3D, t_amount, NULL, 0); }
|
||||
|
||||
/** PATH1 Single */
|
||||
void Renderer::draw(Mesh *t_mesh) { draw(t_mesh, NULL, 0); }
|
||||
|
||||
/// ---
|
||||
|
||||
void Renderer::setCameraDefinitions(Matrix *t_worldView, Vector3 *t_cameraPos, Plane *t_planes)
|
||||
{
|
||||
renderData.worldView = t_worldView;
|
||||
renderData.cameraPosition = t_cameraPos;
|
||||
renderData.frustumPlanes = t_planes;
|
||||
}
|
||||
|
||||
void Renderer::beginFrameIfNeeded()
|
||||
{
|
||||
if (isFrameEmpty)
|
||||
{
|
||||
isFrameEmpty = false;
|
||||
gifSender->sendClear(&zBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::endFrame(float fps)
|
||||
{
|
||||
if (!isFrameEmpty)
|
||||
{
|
||||
if (fps > 49.0F)
|
||||
graph_wait_vsync();
|
||||
flipBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
/** We need to flip buffers outside of the chain, for some reason,
|
||||
* so we use a separate small packet
|
||||
* Do not use this method. This is called via packetManager
|
||||
*/
|
||||
void Renderer::flipBuffers()
|
||||
{
|
||||
graph_set_framebuffer_filtered(
|
||||
frameBuffers[context].address,
|
||||
frameBuffers[context].width,
|
||||
frameBuffers[context].psm,
|
||||
0,
|
||||
0);
|
||||
context ^= 1;
|
||||
isFrameEmpty = 1;
|
||||
qword_t *q = flipPacket->data;
|
||||
q = draw_framebuffer(q, 0, &frameBuffers[context]);
|
||||
q = draw_finish(q);
|
||||
dma_wait_fast();
|
||||
dma_channel_send_normal_ucab(DMA_CHANNEL_GIF, flipPacket->data, q - flipPacket->data, 0);
|
||||
draw_wait_finish();
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/timer.hpp"
|
||||
|
||||
#include <timer.h>
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
Timer::Timer()
|
||||
{
|
||||
this->lastTime = *T3_COUNT;
|
||||
}
|
||||
|
||||
Timer::~Timer() {}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
u32 Timer::getTimeDelta()
|
||||
{
|
||||
this->time = *T3_COUNT;
|
||||
|
||||
if (this->time < this->lastTime) // The counter has wrapped
|
||||
this->change = this->time + (65536 - this->lastTime);
|
||||
else
|
||||
this->change = this->time - this->lastTime;
|
||||
|
||||
this->lastTime = this->time;
|
||||
return this->change;
|
||||
}
|
||||
|
||||
void Timer::primeTimer()
|
||||
{
|
||||
lastTime = *T3_COUNT;
|
||||
}
|
||||
|
||||
float Timer::getFPS()
|
||||
{
|
||||
u32 timeDelta = this->getTimeDelta();
|
||||
|
||||
if (timeDelta == 0)
|
||||
return -1.0F;
|
||||
|
||||
return 15625.0F / (float)timeDelta; // PAL
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/vif_sender.hpp"
|
||||
|
||||
#include <gs_gp.h>
|
||||
#include <dma.h>
|
||||
#include <gif_tags.h>
|
||||
#include "../include/utils/math.hpp"
|
||||
#include "../include/utils/debug.hpp"
|
||||
|
||||
// Similiar set is in PS2SDK, but for VU1 we have to switch ST with RGBAQ, because VU1 must know Q before sending RGBAQ
|
||||
#define DRAW_R_STQ_REGLIST ((u64)GIF_REG_ST) << 0 | ((u64)GIF_REG_RGBAQ) << 4 | ((u64)GIF_REG_XYZ2) << 8
|
||||
const u32 VU1_PACKAGE_VERTS_PER_BUFF = 96; // Remember to modify buffer size in vu1 also
|
||||
const u32 VU1_PACKAGES_PER_PACKET = 6;
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
VifSender::VifSender()
|
||||
{
|
||||
PRINT_LOG("Initializing VifSender");
|
||||
PRINT_LOG("VifSender initialized!");
|
||||
}
|
||||
|
||||
VifSender::~VifSender() {}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// ----
|
||||
|
||||
void VifSender::drawMesh(RenderData *t_renderData, Matrix t_perspective, u32 vertCount2, VECTOR *vertices, VECTOR *normals, VECTOR *coordinates, VECTOR *colors, Mesh *t_mesh, LightBulb *t_bulbs, u16 t_bulbsCount)
|
||||
{
|
||||
if (t_mesh->shouldBeFrustumCulled == 1 && !t_mesh->isInFrustum(t_renderData->frustumPlanes))
|
||||
return;
|
||||
|
||||
vec3ToNative(position, t_mesh->position, 1.0F);
|
||||
vec3ToNative(rotation, t_mesh->rotation, 1.0F);
|
||||
create_local_world(localWorld, position, rotation);
|
||||
create_local_screen(localScreen, localWorld, t_renderData->worldView->data, t_perspective.data);
|
||||
|
||||
// TODO Send it once man xd
|
||||
vu1.sendSingleRefList(0, &localScreen, 4);
|
||||
|
||||
// we have to split 3D object into small parts, because of small memory of VU1
|
||||
for (u32 i = 0; i < vertCount2;)
|
||||
{
|
||||
vu1.createList();
|
||||
for (u8 j = 0; j < VU1_PACKAGES_PER_PACKET; j++) // how many "packages" per one packet
|
||||
{
|
||||
if (i != 0) // we have to go back to avoid the visual artifacts
|
||||
i -= 3;
|
||||
|
||||
const u32 endI = i + (VU1_PACKAGE_VERTS_PER_BUFF - 1) > vertCount2 ? vertCount2 : i + (VU1_PACKAGE_VERTS_PER_BUFF - 1);
|
||||
drawVertices(t_mesh, i, endI, vertices, colors, coordinates, t_renderData->prim);
|
||||
if (endI == vertCount2) // if there are no more vertices to draw, break
|
||||
{
|
||||
i = vertCount2;
|
||||
break;
|
||||
}
|
||||
i += (VU1_PACKAGE_VERTS_PER_BUFF - 1);
|
||||
i++;
|
||||
}
|
||||
vu1.sendList();
|
||||
}
|
||||
}
|
||||
|
||||
/** Draw using PATH1 */
|
||||
void VifSender::drawVertices(Mesh *t_mesh, u32 t_start, u32 t_end, VECTOR *t_vertices, VECTOR *t_colors, VECTOR *t_coordinates, prim_t *t_prim)
|
||||
{
|
||||
const u32 vertCount = t_end - t_start;
|
||||
vu1.addListBeginning();
|
||||
|
||||
// TODO get this via screensettings
|
||||
vu1.addFloat(2048.0F); // scale
|
||||
vu1.addFloat(2048.0F); // scale
|
||||
vu1.addFloat(((float)0xFFFFFF) / 32.0F); // scale
|
||||
vu1.add32(vertCount); // vertex count
|
||||
|
||||
vu1.add128(GIF_SET_TAG(1, 0, 0, 0, GIF_FLG_PACKED, 1), GIF_REG_AD); // 1x set tag
|
||||
|
||||
vu1.add128( // tex -> lod
|
||||
GS_SET_TEX1(
|
||||
t_mesh->spec->lod.calculation,
|
||||
t_mesh->spec->lod.max_level,
|
||||
t_mesh->spec->lod.mag_filter,
|
||||
t_mesh->spec->lod.min_filter,
|
||||
t_mesh->spec->lod.mipmap_select,
|
||||
t_mesh->spec->lod.l,
|
||||
(int)(t_mesh->spec->lod.k * 16.0F)),
|
||||
GS_REG_TEX1);
|
||||
|
||||
vu1.add128( // tex -> buff + clut
|
||||
GS_SET_TEX0(
|
||||
t_mesh->spec->textureBuffer.address >> 6,
|
||||
t_mesh->spec->textureBuffer.width >> 6,
|
||||
t_mesh->spec->textureBuffer.psm,
|
||||
t_mesh->spec->textureBuffer.info.width,
|
||||
t_mesh->spec->textureBuffer.info.height,
|
||||
t_mesh->spec->textureBuffer.info.components,
|
||||
t_mesh->spec->textureBuffer.info.function,
|
||||
t_mesh->spec->clut.address >> 6,
|
||||
t_mesh->spec->clut.psm,
|
||||
t_mesh->spec->clut.storage_mode,
|
||||
t_mesh->spec->clut.start,
|
||||
t_mesh->spec->clut.load_method),
|
||||
GS_REG_TEX0);
|
||||
|
||||
vu1.add128(
|
||||
GS_GIFTAG(
|
||||
vertCount, // amount of loops
|
||||
1,
|
||||
1,
|
||||
GS_PRIM(
|
||||
t_prim->type,
|
||||
t_prim->shading,
|
||||
t_prim->mapping,
|
||||
t_prim->fogging,
|
||||
t_prim->blending,
|
||||
t_prim->antialiasing,
|
||||
t_prim->mapping_type,
|
||||
0, // context
|
||||
t_prim->colorfix),
|
||||
GS_GIFTAG_PACKED,
|
||||
3), // STQ + RGBA + XYZ
|
||||
DRAW_R_STQ_REGLIST);
|
||||
|
||||
for (u8 j = 0; j < 4; j++)
|
||||
vu1.add32(128);
|
||||
|
||||
//// Clipping tests start
|
||||
|
||||
// const float minZ = 1;
|
||||
// const float maxZ = 65535;
|
||||
// const int iGuardDimXY = 2048;
|
||||
|
||||
// vu1.addFloat(1.0F); // TODO clipping maybe there is problem?
|
||||
// vu1.addFloat(1.0F);
|
||||
// vu1.addFloat(1.0F);
|
||||
// vu1.addFloat(1.0F);
|
||||
// float xClip = (float)2048.0f/(drawContext.GetFBWidth() * 0.5f * 2.0f);
|
||||
// packet += Math::Max( xClip, 1.0f );
|
||||
// float yClip = (float)2048.0f/(drawContext.GetFBHeight() * 0.5f * 2.0f);
|
||||
// packet += Math::Max( yClip, 1.0f );
|
||||
// float depthClip = 2048.0f / depthClipToGs;
|
||||
// // FIXME: maybe these 2048's should be 2047.5s...
|
||||
// depthClip *= 1.003f; // round up a bit for fp error (????)
|
||||
// packet += depthClip;
|
||||
// // enable/disable clipping
|
||||
// packet += (drawContext.GetDoClipping()) ? 1 : 0;
|
||||
|
||||
u32 depthBits = 24; // or 28(fog) or 16
|
||||
float depthClipToGs = (float)((1 << depthBits) - 1) / 2.0f;
|
||||
vu1.addFloat(2048.0f / (640.0F * 0.5f * 2.0f));
|
||||
vu1.addFloat(2048.0f / (480.0F * 0.5f * 2.0f));
|
||||
vu1.addFloat((2048.0f / depthClipToGs) * 1.003F);
|
||||
// vu1.addFloat(2048.0F); // scale
|
||||
// vu1.addFloat(2048.0F); // scale
|
||||
// vu1.addFloat(((float)0xFFFFFF) / 32.0F); // scale
|
||||
vu1.addFloat(0.0F);
|
||||
// vu1.addFloat(0.5f * iGuardDimXY);
|
||||
// vu1.addFloat(-0.5f * iGuardDimXY);
|
||||
// vu1.addFloat(1.0F);
|
||||
// vu1.addFloat(500.0F); // far
|
||||
|
||||
//// Clipping tests end
|
||||
|
||||
vu1.addListEnding();
|
||||
vu1.addReferenceList(0, t_vertices + t_start, 2 * vertCount, 1);
|
||||
vu1.addReferenceList(0, t_coordinates + t_start, 2 * vertCount, 1);
|
||||
vu1.addStartProgram();
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
# ______ ____ ___
|
||||
# | \/ ____| |___|
|
||||
# | | | \ | |
|
||||
#-----------------------------------------------------------------------
|
||||
# Copyright 2020, tyra - https://github.com/h4570/tyra
|
||||
# Licenced under Apache License 2.0
|
||||
# Sandro Sobczyński <sandro.sobczynski@gmail.com>
|
||||
*/
|
||||
|
||||
#include "../include/modules/vu1.hpp"
|
||||
|
||||
#include "../include/utils/debug.hpp"
|
||||
#include <dma.h>
|
||||
#include <string.h>
|
||||
#include <kernel.h>
|
||||
|
||||
// ----
|
||||
// Constructors/Destructors
|
||||
// ----
|
||||
|
||||
VU1::VU1()
|
||||
{
|
||||
PRINT_LOG("Initializing VU1");
|
||||
currentBuffer = 0;
|
||||
switchBuffer = 0;
|
||||
PRINT_LOG("VU1 initialized!");
|
||||
}
|
||||
|
||||
VU1::~VU1() {}
|
||||
|
||||
// ----
|
||||
// Methods
|
||||
// -
|
||||
|
||||
/** Create dynamic list */
|
||||
void VU1::createList()
|
||||
{
|
||||
switchBuffer = !switchBuffer;
|
||||
memset((char *)&buildList, 0, sizeof(buildList));
|
||||
|
||||
if (switchBuffer)
|
||||
currentBuffer = (char *)&dmaBuffer1;
|
||||
else
|
||||
currentBuffer = (char *)&dmaBuffer2;
|
||||
buildList.kickBuffer = currentBuffer;
|
||||
}
|
||||
|
||||
/** Add reference list and send via VIF1
|
||||
* Not using TOPS register
|
||||
* Similar to addReferenceList()
|
||||
*/
|
||||
void VU1::sendSingleRefList(int t_destAddress, void *t_data, int t_quadSize)
|
||||
{
|
||||
checkDataAlignment(t_data);
|
||||
|
||||
u8 tempBuffer[32] __attribute__((aligned(16)));
|
||||
void *chain = (u64 *)&tempBuffer; // uncached
|
||||
|
||||
*((u64 *)chain)++ = DMA_REF_TAG((u32)t_data, t_quadSize);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_STCYL, 0, 0x0101);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_UNPACK_V4_32, t_quadSize, t_destAddress);
|
||||
|
||||
*((u64 *)chain)++ = DMA_END_TAG(0);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
|
||||
FlushCache(0);
|
||||
dma_channel_send_chain(DMA_CHANNEL_VIF1, tempBuffer, 0, DMA_FLAG_TRANSFERTAG, 0);
|
||||
dma_channel_wait(DMA_CHANNEL_VIF1, VU1_DMA_CHAN_TIMEOUT);
|
||||
}
|
||||
|
||||
/** Add list beginning, set's double buffer if not set */
|
||||
void VU1::addListBeginning()
|
||||
{
|
||||
if (buildList.isBuilding == 1)
|
||||
PRINT_ERR("Please end current list list before adding new one!");
|
||||
|
||||
if (isDoubleBufferSet == 0)
|
||||
addDoubleBufferSetting();
|
||||
else
|
||||
addFlush();
|
||||
|
||||
buildList.dmaSizeAll += buildList.dmaSize;
|
||||
buildList.dmaSize = 0;
|
||||
buildList.offset = currentBuffer;
|
||||
*((u64 *)currentBuffer)++ = DMA_CNT_TAG(0); // placeholder
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_STCYL, 0, 0x0101); // placeholder
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_UNPACK_V4_32, 0, 0); // placeholder
|
||||
buildList.isBuilding = 1;
|
||||
}
|
||||
|
||||
/** Add list ending and fix unpack size */
|
||||
void VU1::addListEnding()
|
||||
{
|
||||
if (buildList.isBuilding == 0)
|
||||
{
|
||||
PRINT_ERR("Please add list beginning first. Nothing to end!");
|
||||
return;
|
||||
}
|
||||
|
||||
while ((buildList.dmaSize & 0xF))
|
||||
{
|
||||
*((u32 *)currentBuffer)++ = 0;
|
||||
buildList.dmaSize += 4;
|
||||
}
|
||||
|
||||
*((u64 *)buildList.offset)++ = DMA_CNT_TAG(buildList.dmaSize >> 4);
|
||||
*((u32 *)buildList.offset)++ = VIF_CODE(VIF_STCYL, 0, 0x0101);
|
||||
*((u32 *)buildList.offset)++ = AddUnpack(V4_32, 0, buildList.dmaSize >> 4, 1);
|
||||
|
||||
buildList.isBuilding = 0;
|
||||
}
|
||||
|
||||
/** Add list which will load data from given pointer
|
||||
* A lot faster than standard list.
|
||||
* @param offset offset before data in quadwords
|
||||
* @param data data pointer
|
||||
* @param size in quadwords
|
||||
* @param useTops when true, data will be loaded at the beginning of buffer (BASE+OFFSET)
|
||||
*/
|
||||
void VU1::addReferenceList(u32 t_offset, void *t_data, u32 t_size, u8 t_useTops)
|
||||
{
|
||||
checkDataAlignment(t_data);
|
||||
if (buildList.isBuilding == 1)
|
||||
PRINT_ERR("Please end current list list before adding new one!");
|
||||
*((u64 *)currentBuffer)++ = DMA_REF_TAG((u32)t_data, t_size);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_STCYL, 0, 0x0101);
|
||||
*((u32 *)currentBuffer)++ =
|
||||
AddUnpack(V4_32, t_useTops == 1 ? buildList.dmaSize / 16 : t_offset / 16, t_size, t_useTops);
|
||||
buildList.dmaSize += t_size * 8;
|
||||
buildList.dmaSizeAll += buildList.dmaSize;
|
||||
}
|
||||
|
||||
/** Start VU1 program */
|
||||
void VU1::addStartProgram()
|
||||
{
|
||||
*((u64 *)currentBuffer)++ = DMA_CNT_TAG(8 >> 4);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_MSCAL, 0, 0);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_FLUSH, 0, 0);
|
||||
;
|
||||
}
|
||||
|
||||
/** Continue VU1 program from "--cont" line */
|
||||
void VU1::addContinueProgram()
|
||||
{
|
||||
*((u64 *)currentBuffer)++ = DMA_CNT_TAG(8 >> 4);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_MSCAL, 0, 0);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_FLUSH, 0, 0);
|
||||
;
|
||||
}
|
||||
|
||||
/** Add end tag and send packet via VIF1 */
|
||||
void VU1::sendList()
|
||||
{
|
||||
*((u64 *)currentBuffer)++ = DMA_END_TAG(0);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
dma_channel_wait(DMA_CHANNEL_VIF1, VU1_DMA_CHAN_TIMEOUT);
|
||||
dma_channel_send_chain(DMA_CHANNEL_VIF1, buildList.kickBuffer, (u32 *)currentBuffer - (u32 *)buildList.kickBuffer, DMA_FLAG_TRANSFERTAG, 0);
|
||||
}
|
||||
|
||||
void VU1::addDoubleBufferSetting()
|
||||
{
|
||||
*((u64 *)currentBuffer)++ = DMA_CNT_TAG(8 >> 4);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_BASE, 0, 8);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_OFFSET, 0, 496);
|
||||
isDoubleBufferSet = 1;
|
||||
}
|
||||
|
||||
void VU1::addFlush()
|
||||
{
|
||||
*((u64 *)currentBuffer)++ = DMA_CNT_TAG(8 >> 4);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
*((u32 *)currentBuffer)++ = VIF_CODE(VIF_FLUSH, 0, 0);
|
||||
}
|
||||
|
||||
void VU1::checkDataAlignment(void *t_data)
|
||||
{
|
||||
if (((u32)t_data & 0xF))
|
||||
PRINT_ERR("data is not 16 byte aligned!");
|
||||
}
|
||||
|
||||
// ----
|
||||
// List adding
|
||||
// ----
|
||||
|
||||
void VU1::add128(u64 v1, u64 v2)
|
||||
{
|
||||
checkList();
|
||||
*((u64 *)currentBuffer)++ = v1;
|
||||
*((u64 *)currentBuffer)++ = v2;
|
||||
buildList.dmaSize += 16;
|
||||
}
|
||||
|
||||
void VU1::add64(u64 v)
|
||||
{
|
||||
checkList();
|
||||
*((u64 *)currentBuffer)++ = v;
|
||||
buildList.dmaSize += 8;
|
||||
}
|
||||
|
||||
void VU1::add32(u32 v)
|
||||
{
|
||||
checkList();
|
||||
*((u32 *)currentBuffer)++ = v;
|
||||
buildList.dmaSize += 4;
|
||||
}
|
||||
|
||||
void VU1::addFloat(float v)
|
||||
{
|
||||
checkList();
|
||||
*((float *)currentBuffer)++ = v;
|
||||
buildList.dmaSize += 4;
|
||||
}
|
||||
|
||||
void VU1::checkList()
|
||||
{
|
||||
if (buildList.isBuilding == 0)
|
||||
PRINT_ERR("Please add list beginning before adding data!");
|
||||
if (buildList.dmaSizeAll > VIF_BUFFER_SIZE)
|
||||
PRINT_ERR("Buffer size exceed!");
|
||||
}
|
||||
|
||||
// ----
|
||||
// Static
|
||||
// ----
|
||||
|
||||
u8 IS_DMA_VIF1_INITIALIZED = 0;
|
||||
/** TODO */
|
||||
void VU1::uploadProgram(int t_dest, u32 *t_start, u32 *t_end)
|
||||
{
|
||||
if (!IS_DMA_VIF1_INITIALIZED)
|
||||
{
|
||||
IS_DMA_VIF1_INITIALIZED = 1;
|
||||
dma_channel_initialize(DMA_CHANNEL_VIF1, NULL, 0);
|
||||
dma_channel_fast_waits(DMA_CHANNEL_VIF1);
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
u8 tempBuffer[512] __attribute__((aligned(16)));
|
||||
void *chain = (u64 *)&tempBuffer; // uncached
|
||||
|
||||
// get the size of the code as we can only send 256 instructions in each MPGtag
|
||||
count = VU1::countProgramSize(t_start, t_end);
|
||||
while (count > 0)
|
||||
{
|
||||
u32 currentCount = count > 256 ? 256 : count;
|
||||
|
||||
*((u64 *)chain)++ = DMA_REF_TAG((u32)t_start, currentCount / 2);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_MPG, currentCount & 0xFF, t_dest);
|
||||
|
||||
t_start += currentCount * 2;
|
||||
count -= currentCount;
|
||||
t_dest += currentCount;
|
||||
}
|
||||
|
||||
*((u64 *)chain)++ = DMA_END_TAG(0);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
*((u32 *)chain)++ = VIF_CODE(VIF_NOP, 0, 0);
|
||||
|
||||
// Send it to vif1
|
||||
FlushCache(0);
|
||||
dma_channel_send_chain(DMA_CHANNEL_VIF1, tempBuffer, 0, DMA_FLAG_TRANSFERTAG, 0);
|
||||
dma_channel_wait(DMA_CHANNEL_VIF1, VU1_DMA_CHAN_TIMEOUT); // synchronize immediately.
|
||||
}
|
||||
|
||||
u32 VU1::countProgramSize(u32 *t_start, u32 *t_end)
|
||||
{
|
||||
u32 size = (t_end - t_start) / 2;
|
||||
if (size & 1)
|
||||
size++;
|
||||
return size;
|
||||
}
|
||||
Reference in New Issue
Block a user