Просмотр полной версии : GU на PSP
pspowner
24.11.2006, 14:36
Вот, посмотрел доку.
GU_TRIANGLE_STRIP - Filled triangles-strip (3 vertices for the first primitive, 1 for every following)
Т.е. на 1 полигон - 3 точки, на последующие 1. Попробую переделать.
Да, вот ещё что интересное увидел:
void sceGuFrontFace ( int order )
Set the current face-order (for culling).
This only has effect when culling is enabled (GU_CULL_FACE)
Culling order can be:
GU_CW - Clockwise primitives are not culled
GU_CCW - Counter-clockwise are not culled
Parameters:
order - Which order to use
Что это? кто - нить может сказать. Что, как заворачивать примитивы?
Ладно, это сейчас не так важно.
Чайник со СТРИПОМ(cube.zip - для 1.50, кидать в psp/game):
BonifacE
27.11.2006, 12:42
Все эти команды аналогичны командам из обычного opengl, любая отрисовка подразумевает что у тебя есть видимые полигоны и невидимые (т.е. те что отрисовываются за видимыми глубже по z буферу) соотвественно enabled (GU_CULL_FACE) включает возможность проверки на отрисовку полигонов, а sceGuFrontFace собственно включает саму проверку на отображение полигонов переднего плана. GU_TRIANGLE_STRIP - соотвественно тоже полностью аналогичен командам opengl. В том примере что ты мне показывал, попробуй заменить GU_TRIANGLE_STRIP (каждый следующий полигон строится по двум предыдущем точкам и одной новой) на GU_TRIANGLES (каждый следующий полигон строится по трем новым точкам)
pspowner
27.11.2006, 13:13
Ну немного опоздал ты. Хотя всё равно спс. Вроде въехал.
Двусторонние полигоны я уже просёк как строить, сейчас думаю с чем дальше разбираться...
добавлено через 16 минут
Ага, вот ещё.
struct Vertex __attribute__((aligned(16))) vertices[4] =
{
{1, 1, 0xff7f0000,-1,-1,0.0000},
{1, 1, 0xff7f0000,1,1,0.0000},
{1, 1, 0xff7f0000,0,0,1},
{1,1, 0xff7f0000, -1,-1,0}
};
Вот вроде нарисовался двусторонний полигон. Крутится так здорово.
Вопрос другой, как я буду, не отрываясь от предыдущего полигона, отрисовывать примитив, например Куб? Или это как в той детской загадке получается: нарисуйти домик, неотрывая ручку от бумаги и не повторяя нарисованных линий?
Простой полигон состоит всего-лишь из трёх вершин.
В твоём примере это треугольник:
{1, 1, 0xff7f0000,-1,-1,0},
{1, 1, 0xff7f0000,1,1,0},
{1, 1, 0xff7f0000,0,0,1},
Чтобы завершить этот полигон и представить его ввиде квадратной плоскости нам нужно добавить обратный треугольник. Тоесть сомкнуть вершины так, что бы из двух полигонов получилась плоскость в виде квадрата.
Последовательность замыкания треугольников значения не имеет.
BonifacE
27.11.2006, 13:52
TRIANGLE_STRIP нужен для быстрой отрисовки больших обьемов полигонов, например любой ландшафт можно представить в виде одного стрипа, это сильно экономит видео память так как за место 3 вершин грузиш всего одну. Для мало полигональных моделей это не особо эффективно посему, используется чаще всего просто TRIANGLES. Хотя практически любую модельку можно представить в виде стрипа или набора стрипов (в моделерах есть такая галка).
pspowner
27.11.2006, 13:54
Муш, прото что квадрат состоит из двух треугольников - это понятно.
Спрашивал я немонго другое.
Четвёртая точка, в моём случае, делает обратную сторону полигона, если я правильно понял.
Если я добавлю ещё одну точку, то следующий полигон от каких вершин будет строиться?
От последних двух? А если мне надо нарисовать полигон не от этих вершин?
Вот тут и получается что я привязан к последнему нарисованному треугольнику. И если я это не буду учитывать, то на экране будет каша.
И что значит последовательность замыкания? Она всего одна, от последних 2х точек.
Поправьте, если где не прав.
добавлено через 1 минуту
Бони, понятно. Я посмотрю в максе, может есть такая возможность...
Муш, прото что квадрат состоит из двух треугольников - это понятно.
Спрашивал я немонго другое.
Четвёртая точка, в моём случае, делает обратную сторону полигона, если я правильно понял.
Если я добавлю ещё одну точку, то следующий полигон от каких вершин будет строиться?
От последних двух? А если мне надо нарисовать полигон не от этих вершин?
Вот тут и получается что я привязан к последнему нарисованному треугольнику. И если я это не буду учитывать, то на экране будет каша.
И что значит последовательность замыкания? Она всего одна, от последних 2х точек.
Поправьте, если где не прав.
добавлено через 1 минуту
Бони, понятно. Я посмотрю в максе, может есть такая возможность...
Самый простой полигон состоит из 3 вершин. Если ты добавляешь вершину, то ожидать результата можно лишь завершив полигон. Пойми три точки в координатах x,y,z строят тебе твой треугольник. Следующий треугольник соответственно в других координатах. Замыкание - это значит сомкнуть два треугольника хотя бы для того, что бы получить квадратную плоскость.
добавлено через 2 минуты
Возможно в GU можно использовать триангуляцию монотонных полигонов. Это как раз то очем ты говорил "домик не отрывая ручки". Но об этом в GU я не знаю.
pspowner
27.11.2006, 16:10
Гы, значит кубик нарисовался, всё Ок. НО!
Почемуто некоторые треугольники помаргивают... т.е. Такое ощущение что сквозь них что-то пытается пробиться... Хм...
Посмотрите, если кому не в лом...
BonifacE
27.11.2006, 16:25
капитан сорсы с мэйк файлом положи и я гляну...
pspowner
27.11.2006, 16:33
Да собственно это сделано на основе примера из ПСПСДК, только вот в примере всё Ок, а как только я меняю геометрию - всё чёрти как.
pspowner
27.11.2006, 18:17
Уффффф.
Фсё... разобрался, кубик рисуется всё без стрипа и без внутренностей - выглядит отлично и не мерцает.
Дело-то было в одной строчке:
sceGuFrontFace(GU_CCW);
С этим уже можно жить...
Уффффф.
Фсё... разобрался, кубик рисуется всё без стрипа и без внутренностей - выглядит отлично и не мерцает.
Дело-то было в одной строчке:
sceGuFrontFace(GU_CCW);
С этим уже можно жить...
Ну слава всевышнему! =)
Очень рад за тебя! Поздравляю. Существенный прорыв!
pspowner
29.11.2006, 11:54
Муш не смейся, я ведь только учусь :)
Мысли вслух:
Мне захотелось сделат что-то типа 3д движка. Ну чтобы, например, можнон было ходить вокруг этого кубика, смотреть на него с разных сторон.
Но возникли вопросы, над которыми мне стоит основательно задуматься.
Вот так кубик вращается вокруг своей оси:
ScePspFVector3 pos = { 0, 0, -2.5f + movez*0.1};
ScePspFVector3 rot = { valx * 0.79f * (GU_PI/180.0f), valy * 0.98f * (GU_PI/180.0f), valz * 1.32f * (GU_PI/180.0f) };
sceGumRotateXYZ(&rot);
sceGumTranslate(&pos);
Как заставить его вращаться вокруг наблюдателя? Вокруг Viewport?
Поскольку я этого никогда не делал, то возникла у меня идея начать смещение кубика по оси х в зависимости от расстояния от него.
Вот как-то так:
ScePspFVector3 pos = { sin(valy* 0.79f * (GU_PI/180.0f))*(-0.25f+movez*0.1), 0, -2.5f + movez*0.1};
Не знаю, правильно это или нет, но результат есть. Хотя и не совсем такой как ожидалось.
Вобщем, пока в раздумьях я.
К тому же, я наконец-то воткнул в свой компьютер второй гигабайт оперативки и ,О ЧУДО!. Готика 3 стала просто летать - поэтому сейчас часть свободного времени трачу на эту игру. Когда она меня утомит, займусь 3д более плотно.
BonifacE
29.11.2006, 12:12
http://www.gamedev.ru/articles/?id=30038 http://www.gamedev.ru/articles/?id=30039
http://pmg.org.ru/nehe/index.html
...
ScePspFMatrix4 projection;
ScePspFMatrix4 view;
Проекция по желанию
gumLoadIdentity(&projection);
gumPerspective(&projection,75.0f,16.0f/9.0f,0.5f,1000.0f);
Вот тут самое интересное:
{
ScePspFVector3 pos = {0,0,-5.0f};
gumLoadIdentity(&view);
gumTranslate(&view,&pos);
}
Вот тут еще интереснее =)
sceGuSetMatrix(GU_PROJECTION,&projection);
sceGuSetMatrix(GU_VIEW,&view);
Надеюсь ты понял о чем я?
Повторю тут вопрос, который уже задавал, но на который никто не ответил.
Есть PSP Slim с 3.90 M33.
Есть вот эта вот программа:
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <pspdisplay.h>
#include <stdlib.h>
#include <pspgu.h>
#ifndef bool
#define bool char
#define true 1
#define false 0
#endif
PSP_MODULE_INFO("SIMPLY GU MODE",0,1,1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
void dump_threadstatus(void);
bool done=false;
int exit_callback(int arg1,int arg2,void *common)
{
done=true;
return(0);
}
int CallbackThread(SceSize args, void *argp)
{
int cbid;
cbid=sceKernelCreateCallback("Exit Callback",exit_callback,NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return(0);
}
int SetupCallbacks(void)
{
int thid = 0;
thid=sceKernelCreateThread("update_thread",CallbackThread,0x11,0xFA0,0,0);
if(thid>=0) sceKernelStartThread(thid, 0, 0);
return(thid);
}
//начинаем программу
int main(void)
{
SceCtrlData pad;
pspDebugScreenInit();
SetupCallbacks();
sceCtrlSetSamplingCycle(0);
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
sceGuInit();
while(!done)
{
sceCtrlReadBufferPositive(&pad, 1);
//pad.Lx
//pad.Ly
if (pad.Buttons!=0) break;
}
sceGuTerm();
sceKernelExitGame();
return(0);
}
Отлично компилируется, но при запуске на PSP вылетает с ошибкой 8002012С - библиотека не найдена. Это как это так может быть-то?
Настройки IDE вот такие (см. рисунок):
Ёшкин кот! Оказывается, вся проблема была исключительно в линкере. Поменял порядок задания библиотек в ключах- сделал так:
-lpspgum
-lpspgu
-lpspdebug
-lpspdisplay
-lpspctrl
-lpspsdk
-lpsplibc
-lpspnet_inet
-lpspnet_apctl
-lpspnet_resolver
-lpsputility
-lpspuser
-lpspge
-lpspkernel
и всё заработало.
Раньше у меня -lpspgum и -lpspgu шли последними. Интересный прикол...
прикол знатный однако. не встречал подобного... по идее то без раницы должнобыть...
А никто не знает, что в GU представляют собой матрицы GU_VIEW и GU_MODEL? В OpenGL это одна матрица преобразований локальной системы координат и зовётся она GL_MODELVIEW. А как это тут её умудрились разбить на две? Хм...
А как это тут её умудрились разбить на две?
ХЗ
P.S. Раздел начал активно двигаЦЦо!
с огл щас тока разбираюсь. но по идее сони от него не могло уйти далеко... может модель это матрица типа установки вида проектирования в огл?
И ещё вопрос тогда. Вот простейшее GU-приложение:
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <pspdisplay.h>
#include <stdlib.h>
#include <pspgu.h>
#include <pspgum.h>
#include <math.h>
#ifndef bool
#define bool char
#define true 1
#define false 0
#endif
PSP_MODULE_INFO("SIMPLY GU MODE",0,1,1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
static unsigned int __attribute__((aligned(16))) list[262144];
bool done=false;
int exit_callback(int arg1,int arg2,void *common)
{
done=true;
return(0);
}
int CallbackThread(SceSize args, void *argp)
{
int cbid;
cbid=sceKernelCreateCallback("Exit Callback",exit_callback,NULL);
sceKernelRegisterExitCallback(cbid);
sceKernelSleepThreadCB();
return(0);
}
int SetupCallbacks(void)
{
int thid = 0;
thid=sceKernelCreateThread("update_thread",CallbackThread,0x11,0xFA0,0,0);
if(thid>=0) sceKernelStartThread(thid, 0, 0);
return(thid);
}
//начинаем программу
int main(void)
{
SceCtrlData pad;
pspDebugScreenInit();
SetupCallbacks();
sceCtrlSetSamplingCycle(0);
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
sceKernelDcacheWritebackAll();
//---------------------------------------------
//по сути - это немного модифицированный OpenGL
//---------------------------------------------
//инициализируем графику GU
sceGuInit();
//запускаем на выполнение новый контекст дисплея - он должен выполниться сразу, т.к. GU_DIRECT
sceGuStart(GU_DIRECT,list);
//устанавливаем параметры буфера рисования- формат пикселя, указатель на область видеопамяти, длину строки (выровненную, а не физическую)
sceGuDrawBuffer(GU_PSM_8888,(void*)0,512);
//устанавливаем параметры буфера экрана - размер экрана, указатель на видеопамять, длину строки
sceGuDispBuffer(480,272,(void*)0x88000,512);
//устанавливаем параметры буфера глубины- указатель на начало буфера глубины в видеопамяти и длину строки
sceGuDepthBuffer((void*)0x110000,512);
//устанавливаем смещение экрана в общем пространстве 4096x4096 (в PSP такой размер виртуального экрана, судя по всему)
sceGuOffset(2048-(480/2),2048-(272/2));//ставим по центру
//настраиваем видовой порт - порт просмотра- координаты центра и размеры сторон
sceGuViewport(2048,2048,480,272);
//устанавливаем диапазон значений для буфера глубины - передняя и задняя плоскости отсечения (буфер инвертирован и значения от 0 до 65535 !)
sceGuDepthRange(65535,0);
//включаем обрезание области показа по размерам видового порта
sceGuScissor(0,0,480,272);
sceGuEnable(GU_SCISSOR_TEST);
//настроим матрицу проецирования
sceGumMatrixMode(GU_PROJECTION);
sceGumLoadIdentity();
sceGumPerspective(90.0f,16.0/9.0f,0.1f,100.0f);
//включим тест глубины
sceGuDepthFunc(GU_GEQUAL);
sceGuEnable(GU_DEPTH_TEST);
//включим режим гладкой интерполяции цвета граней
sceGuShadeModel(GU_SMOOTH);
//включим режим отсечения граней, повёрнутых обратной стороной к наблюдателю
sceGuFrontFace(GU_CW);
sceGuDisable(GU_CULL_FACE);
//очистим экран и буфер глубины
sceGuClearColor(0x00000000);
sceGuClearDepth(0);
sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT);
//инициализируем матрицы
sceGumMatrixMode(GU_VIEW);
sceGumLoadIdentity();
sceGumMatrixMode(GU_MODEL);
sceGumLoadIdentity();
//теперь можно рисовать
ScePspFVector3 ScePspFVector3_Working;
ScePspFVector3_Working.x=0;
ScePspFVector3_Working.y=0;
ScePspFVector3_Working.z=-10;
sceGumTranslate(&ScePspFVector3_Working);
//выбираем цвет
sceGuColor(0xffffffff);
//распределяем память в текущем дисплейном списке
//как только список выполнится, память освободится
//нельзя создавать массив вершин в обычной памяти, только в памяти списка или как глобальный массив!
//Иначе ничего изображено не будет.
float __attribute__((aligned(16))) *vertex=(float*)sceGuGetMemory(9*sizeof(float));
//треугольник задан в пространстве
vertex[0]=5;//x
vertex[1]=0;//y
vertex[2]=0;//z
vertex[3]=0;//x
vertex[4]=0;//y
vertex[5]=0;//z
vertex[6]=0;//x
vertex[7]=5;//y
vertex[8]=0;//z
//рисуем
sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_TRANSFORM_3D,3,0,vertex);
//а вот так вот можно рисовать с координатами уже спроецированными на плоскость экрана
//выбираем цвет
sceGuColor(0xff00ffff);
vertex[0]=0;//x
vertex[1]=0;//y
vertex[2]=0;//z
vertex[3]=160;//x
vertex[4]=0;//y
vertex[5]=0;//z
vertex[6]=0;//x
vertex[7]=120;//y
vertex[8]=0;//z
//рисуем
sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_TRANSFORM_2D,3,0,vertex);
//и выводим изображение
sceGuFinish();
//ждём, пока дисплейный список (у нас - контекст дисплея) не выполнится
sceGuSync(0,GU_SYNC_DONE);
//включаем дисплей
sceGuDisplay(GU_TRUE);
//делаем видимым буфер, в котором мы рисовали
sceGuSwapBuffers();
//ждём нажатия любой клавиши
while(!done)
{
sceCtrlReadBufferPositive(&pad, 1);
if (pad.Buttons!=0) break;
}
sceGuTerm();
sceKernelExitGame();
return(0);
}
Вопросы такие:
1) Как я понял, работает GU всегда на основе дисплейных списков (как в OpenGL). И для списка нужно явно указывать доступный объём памяти да ещё и с выравниванием по параграфу. Почему-то все задают размер 262144 - это почему так? И вообще, сколько на борту видеопамяти?
2) Почему буфер глубины 16-ти битный? Точность же пострадает...
3) Какой смысл в виртуальном экране 4096x4096, если отображать можно 480x272 максимум и при этом переключать страницы sceGuSwapBuffers(). Зачем же тогда такой экран? Хм... Какой в нём смысл? Единственно, реализовывать полноэкранное сглаживание можно используя полный 4096x4096, но примимо ли это на PSP?
4) Нулевой байт цвета - это альфа-канал?
5) Почему примитивы можно создавать либо глобально в программе, либо распределяя память дисплейного списка командой float __attribute__((aligned(16))) *vertex=(float*)sceGuGetMemory(9*sizeof(float)), но нельзя выделить память типа float vertex[9]. У меня последний вариант не работает никак.
6) Зачем придумано обрезание порта просмотра командами
sceGuScissor(0,0,480,272);
sceGuEnable(GU_SCISSOR_TEST);
7) Для чего нужна команда sceGuDisplay(GU_TRUE); Не понимаю логики. Зачем включать то, что уже включено? Хм...
Вот такие вот пироги... я хорошо знаком с OpenGL, но вот GU меня местами удивляет неимоверно. :)
с огл щас тока разбираюсь. но по идее сони от него не могло уйти далеко... может модель это матрица типа установки вида проектирования в огл?
Нет, для проецирования есть специальная матрица. Я могу вам показать мою старую (года 4 ей уже) реализацию софтверного OpenGL (точнее, части - тестуры я так и не сделал - пропал интерес). Не для PSP. Там конвейер матриц можно посмотреть.
Ээээ. мастер ты это. мыж тут нубы :)а ты к нам будто мы соневские доки читали и знаем...
видеопамяти на борту 2 метра афайк.
Жалко... придётся выяснять опытным путём... Увы, в русском сегменте инета никакой вразумительной информации я не нашёл. А с английским у меня некоторые трудности - я немецкий учил, а английский исключительно "опытным путём". :) Ну ничего, эксперименты покажут, что вся эта тарабарщина из себя представляет. :)
добавлено через 4 часа 17 минут
1) Однако, оказывается, что альфа-канал в цвете идёт первым, а вот дальше идут уже b g и r.
2) Инициализацию локальных и глобальных массивов вершин примитивов можно делать только до sceGuInit. После sceGuInit инициализацию можно делать только через выделенный дисплею блок памяти, через sceGuGetMemory.
3) Чтобы каждую точку примитива раскрасить в свой цвет или чтобы назначить ей точку текстуры,нормали и т.д. нужно объединять флаги команды sceGumDrawArray, например, sceGumDrawArray(GU_TRIANGLES,GU_COLOR_8888|GU_VERTEX_32BITF|GU_TRANSFORM_3D,3,0, sVertex);
Ну а с остальным пока экспериментирую...
Вот что ещё выяснил:
1) При задании текстур данные идут как RGBA (т.е. порядок не такой, как при задании цвета! в sceGuColor)
2) Если синхронизацию по sceDisplayWaitVblankStart() ставить после sceGuSwapBuffers(), то вверху экрана будет полоса, где вывод производиться не будет - отстаём от развёртки? :)
3) Ширина и высота текстуры должны быть степенью 2 и ширина должна быть выровнена по параграфу (16 байт).
Вот код моей библиотеки для работы со спрайтами с использованием GU:
#ifndef CSPRITE_H_INCLUDED
#define CSPRITE_H_INCLUDED
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <pspdisplay.h>
#include <psprtc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pspgu.h>
#include <pspgum.h>
#ifndef bool
#define bool char
#define true 1
#define false 0
#endif
//спрайт
struct SGuSprite
{
int Width;//ширина
int Height;//высота
int WidthImage;//ширина картинки
int HeightImage;//высота картинки
unsigned char *Data;//указатель на данные спрайта
};
//загрузить спрайт
bool GuSprite_LoadSprite(char *FileName,struct SGuSprite *sGuSprite,unsigned char alpha);
//отобразить спрайт
void GuSprite_PutSprite(int x,int y,struct SGuSprite *sGuSprite);
//удалить спрайт
void GuSprite_DeleteSprite(struct SGuSprite *sGuSprite);
//выставить спрайту для цвета (r,g,b) значение alpha
void GuSprite_ReplaceAlpha(unsigned char alpha,unsigned char r,unsigned char g,unsigned char b,struct SGuSprite *sGuSprite);
#endif // CSPRITE_H_INCLUDED
//-------------------------------------------------------------------------
#include "gusprite.h"
struct SGuVertex
{
float U;
float V;
unsigned int Color;
float X;
float Y;
float Z;
};
//заголовок TGA-файла
#pragma pack(1)
struct TGAHEADER
{
char identsize;//размер поля ID заголовка (0)
char colorMapType;//если ли палитра:0-нет,1-есть
char imageType;//тип картинки:0-нет,1-индексные цвета,2-RGB,3-оттенки серого, (3-й бит - RLE-кодирование)
unsigned short colorMapStart;//начало карты цветов
unsigned short colorMapLength;//количество цветов в карте
unsigned char colorMapBits;//размерность палитры
unsigned short xstart;//начальные координаты изображения
unsigned short ystart;
unsigned short width;//размер изображения по X
unsigned short height;//размер изображения по Y
char bits;//количесто бит на пиксель (8,16,24,32)
char de******or;//дескриптор изрображения
};
#pragma pack(8)
//загрузить спрайт (формат - TGA)
bool GuSprite_LoadSprite(char *FileName,struct SGuSprite *sGuSprite,unsigned char alpha)
{
//задаём размеры по-умолчанию
sGuSprite->Data=NULL;
sGuSprite->Width=0;
sGuSprite->Height=0;
//пробуем считать изображение
struct TGAHEADER tgaHeader;
SceUID SceUID_File;
SceUID_File=sceIoOpen(FileName,PSP_O_RDONLY,0777);//открываем файл
if (SceUID_File<0) return(false);//ошибка
//читаем заголовок
if (sceIoRead(SceUID_File,&tgaHeader,sizeof(struct TGAHEADER))<sizeof(struct TGAHEADER))//ошибка - мало данных
{
sceIoClose(SceUID_File);
return(false);
}
//проверяем на возможность чтения
if (tgaHeader.imageType&8)
{
sceIoClose(SceUID_File);
return(false);//RLE не поддерживаем
}
if ((tgaHeader.imageType&7)==0 || (tgaHeader.imageType&7)==3)
{
sceIoClose(SceUID_File);
return(false);//градации серого и отсутствие изображения не поддерживаем
}
//задаём параметры изображения
sGuSprite->Width=tgaHeader.width;
sGuSprite->Height=tgaHeader.height;
//определяем размер изображения, являющегося степенью двойки с широной, кратной 4-м байтам
sGuSprite->WidthImage=1;
sGuSprite->HeightImage=1;
while(sGuSprite->WidthImage<sGuSprite->Width || sGuSprite->WidthImage%4!=0) sGuSprite->WidthImage*=2;
while(sGuSprite->HeightImage<sGuSprite->Height) sGuSprite->HeightImage*=2;
//выделяем память для изображения
int image_length=tgaHeader.width*tgaHeader.height*tgaHeader.bits/8;
unsigned char *i_buffer=(unsigned char*)malloc(image_length);
//считываем изображение
sceIoLseek(SceUID_File,sizeof(struct TGAHEADER)+tgaHeader.colorMapStart+tgaHeader.colorMapLength*tgaHeader.colorMapBi ts/8,SEEK_SET);
if(sceIoRead(SceUID_File,i_buffer,image_length)<image_length)
{
sceIoClose(SceUID_File);
free(i_buffer);
return(false);
}
//а теперь анализируем формат
if (tgaHeader.bits==24)//BGR - модицифируем для четвёрок байт
{
sceIoClose(SceUID_File);
unsigned char *out_image=(unsigned char*)malloc(sGuSprite->WidthImage*sGuSprite->HeightImage*4);
int y,x;
if (tgaHeader.de******or==32)//прямой формат
{
unsigned char *oi_ptr=out_image;
unsigned char *i_ptr=i_buffer;
for(y=0;y<tgaHeader.height;y++,i_ptr+=tgaHeader.width*3,oi_ptr+=sGuSprite->WidthImage*4)
{
unsigned char *i_ptrc=i_ptr;
unsigned char *oi_ptrc=oi_ptr;
for(x=0;x<tgaHeader.width;x++)
{
unsigned char b=*(i_ptrc);i_ptrc++;
unsigned char g=*(i_ptrc);i_ptrc++;
unsigned char r=*(i_ptrc);i_ptrc++;
unsigned char a=alpha;
*oi_ptrc=r;oi_ptrc++;
*oi_ptrc=g;oi_ptrc++;
*oi_ptrc=b;oi_ptrc++;
*oi_ptrc=a;oi_ptrc++;
}
}
}
if (tgaHeader.de******or==8)//обратный формат
{
unsigned char *oi_ptr=out_image;
unsigned char *i_ptr=i_buffer+tgaHeader.width*tgaHeader.height*3-1;
for(y=tgaHeader.height-1;y>=0;y--,i_ptr-=tgaHeader.width*3,oi_ptr+=sGuSprite->WidthImage*4)
{
unsigned char *i_ptrc=i_ptr;
unsigned char *oi_ptrc=oi_ptr;
for(x=0;x<tgaHeader.width;x++)
{
unsigned char b=*(i_ptrc);i_ptrc++;
unsigned char g=*(i_ptrc);i_ptrc++;
unsigned char r=*(i_ptrc);i_ptrc++;
unsigned char a=alpha;
*oi_ptrc=r;oi_ptrc++;
*oi_ptrc=g;oi_ptrc++;
*oi_ptrc=b;oi_ptrc++;
*oi_ptrc=a;oi_ptrc++;
}
}
}
free(i_buffer);
sGuSprite->Data=out_image;
return(true);
}
if (tgaHeader.colorMapType==1 && tgaHeader.colorMapBits/8==3)//есть палитра по 24 бита
{
sceIoLseek(SceUID_File,tgaHeader.colorMapStart+sizeof(struct TGAHEADER),SEEK_SET);
//читаем палитру
unsigned char *color_map=(unsigned char*)malloc(tgaHeader.colorMapLength*3);
if (sceIoRead(SceUID_File,color_map,tgaHeader.colorMapLength*3)<tgaHeader.colorMapLength*3)
{
sceIoClose(SceUID_File);
free(color_map);
free(i_buffer);
return(false);
}
//нам потребуется изменить формат
unsigned char *out_image=(unsigned char*)malloc(sGuSprite->WidthImage*sGuSprite->HeightImage*4);
int y,x;
if (tgaHeader.de******or==32)//прямой формат
{
unsigned char *oi_ptr=out_image;
unsigned char *i_ptr=i_buffer;
for(y=0;y<tgaHeader.height;y++,i_ptr+=tgaHeader.width,oi_ptr+=sGuSprite->WidthImage*4)
{
unsigned char *i_ptrc=i_ptr;
unsigned char *oi_ptrc=oi_ptr;
for(x=0;x<tgaHeader.width;x++,i_ptrc++)
{
int index=(*i_ptrc)*3;
unsigned char b=color_map[index];
unsigned char g=color_map[index+1];
unsigned char r=color_map[index+2];
unsigned char a=alpha;
*oi_ptrc=r;oi_ptrc++;
*oi_ptrc=g;oi_ptrc++;
*oi_ptrc=b;oi_ptrc++;
*oi_ptrc=a;oi_ptrc++;
}
}
}
if (tgaHeader.de******or==8)//формат перевёрнутый
{
unsigned char *oi_ptr=out_image;
unsigned char *i_ptr=i_buffer+tgaHeader.width*(tgaHeader.height-1);
for(y=tgaHeader.height-1;y>=0;y--,i_ptr-=tgaHeader.width,oi_ptr+=sGuSprite->WidthImage*4)
{
unsigned char *i_ptrc=i_ptr;
unsigned char *oi_ptrc=oi_ptr;
for(x=0;x<tgaHeader.width;x++,i_ptrc++)
{
int index=(*i_ptrc)*3;
unsigned char b=color_map[index];
unsigned char g=color_map[index+1];
unsigned char r=color_map[index+2];
unsigned char a=alpha;
*oi_ptrc=r;oi_ptrc++;
*oi_ptrc=g;oi_ptrc++;
*oi_ptrc=b;oi_ptrc++;
*oi_ptrc=a;oi_ptrc++;
}
}
}
free(i_buffer);
free(color_map);
sGuSprite->Data=out_image;
sceIoClose(SceUID_File);
return(true);
}
//иные режимы не поддерживаем
sceIoClose(SceUID_File);
free(i_buffer);
return(false);
}
//отобразить спрайт
void GuSprite_PutSprite(int x,int y,struct SGuSprite *sGuSprite)
{
if (sGuSprite->Data==NULL || sGuSprite->Width==0 || sGuSprite->Height==0) return;//спрайт отсутствует
sceGuTexMode(GU_PSM_8888,0,0,0);
sceGuTexImage(0,sGuSprite->WidthImage,sGuSprite->HeightImage,sGuSprite->WidthImage,sGuSprite->Data);
sceGuTexFunc(GU_TFX_MODULATE,GU_TCC_RGBA);
sceGuTexFilter(GU_NEAREST,GU_NEAREST);
sceGuTexWrap(GU_CLAMP,GU_CLAMP);
sceGuTexScale(1,1);
sceGuTexOffset(0,0);
struct SGuVertex *sGuVertex;
sGuVertex=sceGuGetMemory(2*sizeof(struct SGuVertex));
sGuVertex[0].X=x;
sGuVertex[0].Y=y;
sGuVertex[0].Z=0;
sGuVertex[0].Color=0xffffffff;
sGuVertex[0].U=0;
sGuVertex[0].V=0;
sGuVertex[1].X=x+sGuSprite->Width;
sGuVertex[1].Y=y+sGuSprite->Height;
sGuVertex[1].Z=0;
sGuVertex[1].Color=0xffffffff;
sGuVertex[1].U=sGuSprite->Width;
sGuVertex[1].V=sGuSprite->Height;
sceGumDrawArray(GU_SPRITES,GU_TEXTURE_32BITF|GU_COLOR_8888|GU_VERTEX_32BITF|GU_T RANSFORM_2D,2,0,sGuVertex);
}
//удалить спрайт
void GuSprite_DeleteSprite(struct SGuSprite *sGuSprite)
{
if (sGuSprite->Data==NULL || sGuSprite->Width==0 || sGuSprite->Height==0) return;//спрайт отсутствует
free(sGuSprite->Data);
sGuSprite->Data=NULL;
sGuSprite->Width=0;
sGuSprite->Height=0;
}
//выставить спрайту для цвета (r,g,b) значение alpha
void GuSprite_ReplaceAlpha(unsigned char alpha,unsigned char r,unsigned char g,unsigned char b,struct SGuSprite *sGuSprite)
{
if (sGuSprite->Data==NULL || sGuSprite->Width==0 || sGuSprite->Height==0) return;//спрайт отсутствует
unsigned char *ptr=sGuSprite->Data;
int n;
int length=sGuSprite->WidthImage*sGuSprite->HeightImage;
for(n=0;n<length;n++)
{
unsigned char ri=*ptr;ptr++;
unsigned char gi=*ptr;ptr++;
unsigned char bi=*ptr;ptr++;
if (ri==r && gi==g && bi==b) *ptr=alpha;
ptr++;
}
}
А вот пример приложения-заготовки для пасьянса с двумя спрайтами, причём, второй спрайт (указатель) имеет область прозрачности.
А какой смысл в пикселе интресно порядок менять?
Хороший вопрос... Наверное, из целей совместимости с OpenGL. А вообще, картинки всякие (TGA и BMP и другие) тоже порядок цветов меняют. Я уже и не помню, зачем им это надо было... Но вообще, это не очень удобно лично мне. :)
добавлено через 1 минуту
Ах да, я сказал несколько неоднозначно:
"Однако, оказывается, что альфа-канал в цвете идёт первым, а вот дальше идут уже b g и r. "
Т.е. раскладка битовая такая: abgr.
А для текстуры или спрайта раскладка: rgba
может во время какихто прередач(внутре конвеера или из памяти в гу...) удобно чтобы байты задом наперёд шли...хых. мистика :)
однако ценное наблюдение. спасибо.
Да нет, вроде конвейеру-то как раз всё равно... Может, там для прямого вывода загруженных картинок сделано? То есть, читаем, скажем, bmp и тут же выводим простым копированием в видеопамять. Хотя вряд ли. Надо поискать смысл инверсии порядка цветов.
Отправил GU текстурку 480x272 по-моему, а по его представлениям 512x512. Ну что можно сказать... либо я что-то сделал не так, либо он с такими размерами не работает. Экран мерцает, по нему идут полосы, хотя на заднем фоне текстуру видно. Увеличивал размер памяти дисплейного списка - не помогло. А с маленькими 64x64 без проблем всё идёт. Наверное, 512 для него слишком жирно...
А вот GU-приложение "пасьянс косынка". Пока - прототип. То есть, играть можно (аналоговый джойстик и кнопка "крестик"). Это на нём текстуру зелёного сукна вместо 480x272 мне пришлось заменить маленький кусочек. :)
Занятно. Попытался вывести несколько раз относительно крупный спрайт (256x64). Опять глюки. Экран мерцает, картинка разваливается. Причём, один спрайт выводится. Два - уже нет. Такое впечатление, что Gu массив данных спрайта зачем-то перекидывает в память списка и заданных 256 кБ не хватает. Но если выделить в 10 раз больше, тоже работать не начинает. Если выводить один спрайт в одном дисплейном списке, а второй во втором, то тоже ничего не выходит, либо я чего-то не так делаю.
Вот сейчас сделал спрайты 128x64 и выделил под список 512 кБ. Пока работает. Интересно, если я ещё спрайтов добавлю, оно продолжит работать... хм..
А всё-таки непонятно, как же выглядит адресное пространство PSP. Я попытался снять скриншот в моём пасьянсе копированием области, заданной GU как видеобуфер (т.е. начинающейся с 0 (!) - обычно-то для графических приложений не для GU задавался адрес 64 мБ)
<...>
static unsigned int staticOffset=0;
static unsigned int getMemorySize(unsigned int width,unsigned int height,unsigned int psm)
{
switch (psm)
{
case GU_PSM_T4: return((width*height)>>1);
case GU_PSM_T8: return(width*height);
case GU_PSM_5650:
case GU_PSM_5551:
case GU_PSM_4444:
case GU_PSM_T16: return(2*width*height);
case GU_PSM_8888:
case GU_PSM_T32: return(4*width*height);
default: return(0);
}
}
void* getStaticVramBuffer(unsigned int width,unsigned int height,unsigned int psm)
{
unsigned int memSize=getMemorySize(width,height,psm);
void* result=(void*)staticOffset;
staticOffset+=memSize;
return(result);
}
void* getStaticVramTexture(unsigned int width,unsigned int height,unsigned int psm)
{
void* result=getStaticVramBuffer(width,height,psm);
return((void*)(((unsigned int)result) + ((unsigned int)sceGeEdramGetAddr())));
}
<...>
void* fbp0=getStaticVramBuffer(512,272,GU_PSM_8888);
void* fbp1=getStaticVramBuffer(512,272,GU_PSM_8888);
void* zbp=getStaticVramBuffer(512,272,GU_PSM_4444);
//инициализируем графику GU
sceGuInit();
//запускаем на выполнение новый контекст дисплея - он должен выполниться сразу, т.к. GU_DIRECT
sceGuStart(GU_DIRECT,list);
//устанавливаем параметры буфера рисования- формат пикселя, указатель на область видеопамяти, длину строки (выровненную, а не физическую)
sceGuDrawBuffer(GU_PSM_8888,fbp0,512);
//устанавливаем параметры буфера экрана - размер экрана, указатель на видеопамять, длину строки
sceGuDispBuffer(480,272,fbp1,512);
//устанавливаем параметры буфера глубины- указатель на начало буфера глубины в видеопамяти и длину строки
sceGuDepthBuffer(zbp,512);
и ничего не вышло. Точнее, я выделил память через malloc и туда сделал memcpy с адреса fbp0 (и с fbp1 тоже пробовал). Приставка вырубается и перезагружается. Но ведь когда я работал с видео через прямое обращение к видеопамяти, то самое в 64 мБ, то всё работало отлично. Не понимаю я, где же реально расположена видеопамять и как так получается, что я могу указать видеопамять на каких-то адресах, где по какой-то случайности не попадает кусок моей программы? Я пытался GU дать адрес равный 64 мБ - на экране хаос точек. Эх, знать бы архитектуру и принципы управления памятью этого чуда...
Даю справку. по адресам:
http://wiki.ps2dev.org/psp:memory_map
Надеюсь это то. на слимке чутка отличается но не критично. чуть пошире куски основной памяти и безкешевой области.
думаю начинать копировать с 0 бесполезно, для неё это ркивой указатель и она сразу его в топку. АФАЙК начинать надо с 64Кб.
Также советую заглянуть в папку с семплами, там был пример как гулять по памяти, делать карту и как дампить всякое разное.
думаю начинать копировать с 0 бесполезно, для неё это ркивой указатель и она сразу его в топку. АФАЙК начинать надо с 64Кб.
Однако, для GU этот указатель вполне рабочий. И Совершенно не понятно, почему он в нуле, а следующий за ним тоже затирает совсем не свободную область памяти.
Вопрос без официальных доков имхо не отвечаем :) те как факт мы это знаем. а вот почему? ну хз. так уж сделано.
А ещё никто у Sony оригинальный SKD не спиратил? :)
Пока нет :( по крайней мере в торентах не ищутся.
Я-таки перешёл на Си++. Теперь надо отыскать операторы new и delete. :) Никто не знает, как их подключить на PSP?
Хотя нашёл в чём дело. Нужно в makefile подключать и -lstdc++.
Т.е. писать: LIBS = -lm -lstdc++
Пишу теперь в чистом 3D. Выяснил занимательную вещь - тест глубины обратный по смыслу должен быть. Буфер инвертирован sceGuDepthRange(65535,0);, вот и глубина от масимальной (у наблюдателя) до минимальной (в бесконечности). Т.е. вместо
sceGuDepthFunc(GU_LEQUAL); (рисовать, если меньшая глубина), нужно использовать sceGuDepthFunc(GU_GEQUAL); (рисовать, если большая). А я-то не врубаюсь, почему картинки нет? Вроде была раньше. А сам давным-давно заменил на LEQUAL по аналогии с OpenGL. :) Проверил sceGuDepthRange(0,65535);. Не работает LEQUAL. Значит, буфер действительно инвертирован.
добавлено через 1 час 21 минуту
Нифига себе Z-буффер у этой приставки! У неё банально точности может не хватить. Делаешь sceGumPerspective(30.0f,16.0/9.0f,1.0f,1000.0f);, т.е. передняя и задняя плоскости отсечения: 1 - 1000, соответственно. Появляются артефакты на дистанциях -100 и дальше. Просто ошибка 16-ти битного Z-буфера. Пришлось уменьшить диапазон до 50 - 200.
А вот интересно, почему при работе с GU иногда (и довольно часто) после изменения строчки в программе (практически любой, причём, даже не относящейся к графике) и перекомпиляции, программа на PSP идёт очень странно. Экран раздваивается на левую и правую половины, в каждой из которых по выводимой программой картинке, только они уменьшены и цвета искажены. С чем это связано-то? А потом добавишь ещё что-нибудь в программу и эффект исчезает. Видимо, эффект связан с какой-то длиной в программе. В одних случаях где-то блок в памяти ложится одним способом, а в других как-то иначе, с другим выравниванием, например.
чтонить нарушает работу 3д конвеера имхо. сам такое видел пару раз.
vBulletin® v3.8.7, Copyright ©2000-2025, vBulletin Solutions, Inc. Перевод: zCarot