Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
70db96a52e | ||
|
|
1c9b1b1969 |
No files matched your search
Executable → Regular
+52
-392
@@ -1,402 +1,62 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
# Built application files
|
||||||
## files generated by popular Visual Studio add-ons.
|
*.apk
|
||||||
##
|
*.ap_
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
*.aab
|
||||||
|
|
||||||
.vscode/
|
# Files for the ART/Dalvik VM
|
||||||
|
*.dex
|
||||||
|
|
||||||
# User-specific files
|
# Java class files
|
||||||
*.rsuser
|
*.class
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# Generated files
|
||||||
*.userprefs
|
bin/
|
||||||
|
gen/
|
||||||
|
out/
|
||||||
|
|
||||||
# Mono auto generated files
|
# Gradle files
|
||||||
mono_crash.*
|
.gradle/
|
||||||
.........................................................................
|
build/
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
[Ww][Ii][Nn]32/
|
|
||||||
[Aa][Rr][Mm]/
|
|
||||||
[Aa][Rr][Mm]64/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Ll]og/
|
|
||||||
[Ll]ogs/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUnit
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
nunit-*.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
|
|
||||||
# ASP.NET Scaffolding
|
|
||||||
ScaffoldingReadMe.txt
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_h.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*_wpftmp.csproj
|
|
||||||
*.log
|
|
||||||
*.tlog
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Coverlet is a free, cross platform Code Coverage Tool
|
|
||||||
coverage*.json
|
|
||||||
coverage*.xml
|
|
||||||
coverage*.info
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# NuGet Symbol Packages
|
|
||||||
*.snupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Nuget personal access tokens and Credentials
|
|
||||||
nuget.config
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
*.appxbundle
|
|
||||||
*.appxupload
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!?*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*~
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
*- [Bb]ackup.rdl
|
|
||||||
*- [Bb]ackup ([0-9]).rdl
|
|
||||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
node_modules/
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# CodeRush personal settings
|
|
||||||
.cr/personal
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
|
|
||||||
# Local History for Visual Studio
|
|
||||||
.localhistory/
|
|
||||||
|
|
||||||
# BeatPulse healthcheck temp database
|
|
||||||
healthchecksdb
|
|
||||||
|
|
||||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
|
||||||
MigrationBackup/
|
|
||||||
|
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
|
||||||
.ionide/
|
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
|
||||||
FodyWeavers.xsd
|
|
||||||
|
|
||||||
# VS Code files for those working on multiple tools
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
*.code-workspace
|
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
|
||||||
.history/
|
|
||||||
|
|
||||||
# Windows Installer files from build outputs
|
|
||||||
*.cab
|
|
||||||
*.msi
|
|
||||||
*.msix
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
|
|
||||||
# JetBrains Rider
|
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Local configuration file (sdk path, etc)
|
||||||
|
local.properties
|
||||||
|
|
||||||
sdl_app
|
# CMake build files
|
||||||
|
CMakeFiles/
|
||||||
|
cmake_install.cmake
|
||||||
|
CMakeCache.txt
|
||||||
|
Makefile
|
||||||
|
*.cmake
|
||||||
|
|
||||||
*.sln.iml
|
# Android Studio
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
.cxx/
|
||||||
|
.gradle/
|
||||||
|
.DS_Store
|
||||||
|
/captures/
|
||||||
|
.externalNativeBuild/
|
||||||
|
|
||||||
images.zip
|
# NDK
|
||||||
script.py
|
.obj/
|
||||||
|
*.o
|
||||||
|
*.so
|
||||||
|
|
||||||
jumpingbird.*
|
# SDL2 (если используешь)
|
||||||
jumpingbird.data
|
libs/
|
||||||
build
|
obj/
|
||||||
build-emcmake
|
|
||||||
|
# Временные файлы
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# Проектные специфичные
|
||||||
|
app/build/
|
||||||
|
*.log
|
||||||
|
app/jni/libpng
|
||||||
|
app/jni/SDL
|
||||||
|
app/jni/zlib
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
#ifdef AUDIO
|
|
||||||
|
|
||||||
#include "AudioPlayerAsync.h"
|
|
||||||
|
|
||||||
|
|
||||||
AudioPlayerAsync::AudioPlayerAsync() : worker(&AudioPlayerAsync::workerThread, this) {}
|
|
||||||
|
|
||||||
AudioPlayerAsync::~AudioPlayerAsync() {
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
|
||||||
stop = true;
|
|
||||||
cv.notify_all();
|
|
||||||
}
|
|
||||||
worker.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioPlayerAsync::stopAsync() {
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
|
||||||
taskQueue.push([this]() {
|
|
||||||
//audioPlayerMutex.lock();
|
|
||||||
audioPlayer->stop();
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
});
|
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioPlayerAsync::resetAsync() {
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
|
||||||
taskQueue.push([this]() {
|
|
||||||
//audioPlayerMutex.lock();
|
|
||||||
audioPlayer.reset();
|
|
||||||
audioPlayer = std::make_unique<AudioPlayer>();
|
|
||||||
|
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
});
|
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioPlayerAsync::playSoundAsync(std::string soundName) {
|
|
||||||
|
|
||||||
soundNameMutex.lock();
|
|
||||||
latestSoundName = soundName;
|
|
||||||
soundNameMutex.unlock();
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
|
||||||
taskQueue.push([this]() {
|
|
||||||
//audioPlayerMutex.lock();
|
|
||||||
if (audioPlayer) {
|
|
||||||
audioPlayer->playSound(latestSoundName);
|
|
||||||
}
|
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
});
|
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioPlayerAsync::playMusicAsync(std::string musicName) {
|
|
||||||
|
|
||||||
musicNameMutex.lock();
|
|
||||||
latestMusicName = musicName;
|
|
||||||
musicNameMutex.unlock();
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
|
||||||
taskQueue.push([this]() {
|
|
||||||
//audioPlayerMutex.lock();
|
|
||||||
if (audioPlayer) {
|
|
||||||
audioPlayer->playMusic(latestMusicName);
|
|
||||||
}
|
|
||||||
//audioPlayerMutex.unlock();
|
|
||||||
});
|
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioPlayerAsync::workerThread() {
|
|
||||||
while (true) {
|
|
||||||
std::function<void()> task;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(mtx);
|
|
||||||
cv.wait(lock, [this]() { return !taskQueue.empty() || stop; });
|
|
||||||
|
|
||||||
if (stop && taskQueue.empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
task = taskQueue.front();
|
|
||||||
taskQueue.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
task();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#ifdef AUDIO
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <queue>
|
|
||||||
#include <functional>
|
|
||||||
#include "cmakeaudioplayer/include/AudioPlayer.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
class AudioPlayerAsync {
|
|
||||||
public:
|
|
||||||
AudioPlayerAsync();
|
|
||||||
~AudioPlayerAsync();
|
|
||||||
|
|
||||||
void resetAsync();
|
|
||||||
|
|
||||||
void playSoundAsync(std::string soundName);
|
|
||||||
|
|
||||||
void playMusicAsync(std::string musicName);
|
|
||||||
|
|
||||||
void stopAsync();
|
|
||||||
|
|
||||||
void exit()
|
|
||||||
{
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread worker;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<AudioPlayer> audioPlayer;
|
|
||||||
//std::mutex audioPlayerMutex;
|
|
||||||
|
|
||||||
std::mutex soundNameMutex;
|
|
||||||
std::mutex musicNameMutex;
|
|
||||||
|
|
||||||
std::string latestSoundName;
|
|
||||||
std::string latestMusicName;
|
|
||||||
|
|
||||||
std::mutex mtx;
|
|
||||||
std::condition_variable cv;
|
|
||||||
std::queue<std::function<void()>> taskQueue;
|
|
||||||
bool stop = false;
|
|
||||||
|
|
||||||
void workerThread();
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
#include "Game.h"
|
|
||||||
#include "AnimatedModel.h"
|
|
||||||
#include "BoneAnimatedModel.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include "TextureManager.h"
|
|
||||||
#include "TextModel.h"
|
|
||||||
|
|
||||||
namespace ZL
|
|
||||||
{
|
|
||||||
const char* CONST_ZIP_FILE = "";
|
|
||||||
|
|
||||||
Game::Game()
|
|
||||||
: window(nullptr)
|
|
||||||
, glContext(nullptr)
|
|
||||||
, newTickCount(0)
|
|
||||||
, lastTickCount(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::~Game() {
|
|
||||||
if (glContext) {
|
|
||||||
SDL_GL_DeleteContext(glContext);
|
|
||||||
}
|
|
||||||
if (window) {
|
|
||||||
SDL_DestroyWindow(window);
|
|
||||||
}
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::setup() {
|
|
||||||
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
|
||||||
|
|
||||||
ZL::BindOpenGlFunctions();
|
|
||||||
ZL::CheckGlError();
|
|
||||||
|
|
||||||
// Initialize renderer
|
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
|
||||||
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE);
|
|
||||||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE);
|
|
||||||
#else
|
|
||||||
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE);
|
|
||||||
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_desktop.fragment", CONST_ZIP_FILE);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//Load texture
|
|
||||||
spaceshipTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sship001x.png"));
|
|
||||||
spaceshipBase = LoadFromTextFile02("./resources/spaceship004.txt");
|
|
||||||
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
|
|
||||||
spaceship.AssignFrom(spaceshipBase);
|
|
||||||
spaceship.RefreshVBO();
|
|
||||||
|
|
||||||
|
|
||||||
renderer.InitOpenGL();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::drawScene() {
|
|
||||||
static const std::string defaultShaderName = "default";
|
|
||||||
static const std::string vPositionName = "vPosition";
|
|
||||||
static const std::string vTexCoordName = "vTexCoord";
|
|
||||||
static const std::string textureUniformName = "Texture";
|
|
||||||
|
|
||||||
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
|
|
||||||
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
glViewport(0, 0, Environment::width, Environment::height);
|
|
||||||
|
|
||||||
CheckGlError();
|
|
||||||
|
|
||||||
renderer.shaderManager.PushShader(defaultShaderName);
|
|
||||||
renderer.RenderUniform1i(textureUniformName, 0);
|
|
||||||
renderer.EnableVertexAttribArray(vPositionName);
|
|
||||||
renderer.EnableVertexAttribArray(vTexCoordName);
|
|
||||||
|
|
||||||
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
|
||||||
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
|
|
||||||
1, 1000);
|
|
||||||
renderer.PushMatrix();
|
|
||||||
|
|
||||||
renderer.LoadIdentity();
|
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f*Environment::zoom });
|
|
||||||
renderer.RotateMatrix(QuatFromRotateAroundX(M_PI/6.0));
|
|
||||||
//renderer.RotateMatrix(QuatFromRotateAroundX(Environment::cameraAlpha));
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
|
||||||
renderer.DrawVertexRenderStruct(spaceship);
|
|
||||||
|
|
||||||
renderer.PopMatrix();
|
|
||||||
renderer.PopProjectionMatrix();
|
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
|
||||||
renderer.DisableVertexAttribArray(vTexCoordName);
|
|
||||||
|
|
||||||
renderer.shaderManager.PopShader();
|
|
||||||
|
|
||||||
CheckGlError();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::processTickCount() {
|
|
||||||
|
|
||||||
if (lastTickCount == 0) {
|
|
||||||
lastTickCount = SDL_GetTicks64();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
newTickCount = SDL_GetTicks64();
|
|
||||||
if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) {
|
|
||||||
size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ?
|
|
||||||
CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount;
|
|
||||||
|
|
||||||
//gameObjects.updateScene(delta);
|
|
||||||
|
|
||||||
Environment::cameraAlpha = Environment::cameraAlpha + delta * M_PI / 10000.f;
|
|
||||||
|
|
||||||
lastTickCount = newTickCount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::render() {
|
|
||||||
SDL_GL_MakeCurrent(ZL::Environment::window, glContext);
|
|
||||||
ZL::CheckGlError();
|
|
||||||
|
|
||||||
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
drawScene();
|
|
||||||
processTickCount();
|
|
||||||
|
|
||||||
SDL_GL_SwapWindow(ZL::Environment::window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Game::update() {
|
|
||||||
SDL_Event event;
|
|
||||||
while (SDL_PollEvent(&event)) {
|
|
||||||
if (event.type == SDL_QUIT) {
|
|
||||||
Environment::exitGameLoop = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (event.type == SDL_MOUSEWHEEL) {
|
|
||||||
static const float zoomstep = 2.0f;
|
|
||||||
if (event.wheel.y > 0) {
|
|
||||||
Environment::zoom -= zoomstep;
|
|
||||||
}
|
|
||||||
else if (event.wheel.y < 0) {
|
|
||||||
Environment::zoom += zoomstep;
|
|
||||||
}
|
|
||||||
if (Environment::zoom < zoomstep) {
|
|
||||||
Environment::zoom = zoomstep;
|
|
||||||
}
|
|
||||||
/*if (Environment::zoom > 4) {
|
|
||||||
Environment::zoom = 4;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//this->modelMeshRender.data.Scale(0.5);
|
|
||||||
//this->modelMeshRender.RefreshVBO();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
render();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ZL
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "OpenGlExtensions.h"
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include "Environment.h"
|
|
||||||
#include "TextureManager.h"
|
|
||||||
|
|
||||||
namespace ZL {
|
|
||||||
|
|
||||||
class Game {
|
|
||||||
public:
|
|
||||||
Game();
|
|
||||||
~Game();
|
|
||||||
|
|
||||||
void setup();
|
|
||||||
void update();
|
|
||||||
void render();
|
|
||||||
|
|
||||||
bool shouldExit() const { return Environment::exitGameLoop; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void processTickCount();
|
|
||||||
void drawScene();
|
|
||||||
|
|
||||||
SDL_Window* window;
|
|
||||||
SDL_GLContext glContext;
|
|
||||||
Renderer renderer;
|
|
||||||
|
|
||||||
size_t newTickCount;
|
|
||||||
size_t lastTickCount;
|
|
||||||
|
|
||||||
static const size_t CONST_TIMER_INTERVAL = 10;
|
|
||||||
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
|
||||||
|
|
||||||
std::shared_ptr<Texture> spaceshipTexture;
|
|
||||||
VertexDataStruct spaceshipBase;
|
|
||||||
VertexRenderStruct spaceship;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ZL
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
#include "OpenGlExtensions.h"
|
|
||||||
|
|
||||||
#include "Utils.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
|
||||||
|
|
||||||
//====================================================
|
|
||||||
//===================== GLSL Shaders =================
|
|
||||||
//====================================================
|
|
||||||
|
|
||||||
//Requires GL_VERSION_2_0
|
|
||||||
PFNGLCREATEPROGRAMPROC glCreateProgram = NULL;
|
|
||||||
PFNGLDELETEPROGRAMPROC glDeleteProgram = NULL;
|
|
||||||
PFNGLLINKPROGRAMPROC glLinkProgram = NULL;
|
|
||||||
PFNGLVALIDATEPROGRAMPROC glValidateProgram = NULL;
|
|
||||||
PFNGLUSEPROGRAMPROC glUseProgram = NULL;
|
|
||||||
PFNGLGETPROGRAMIVPROC glGetProgramiv = NULL;
|
|
||||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = NULL;
|
|
||||||
PFNGLCREATESHADERPROC glCreateShader = NULL;
|
|
||||||
PFNGLDELETESHADERPROC glDeleteShader = NULL;
|
|
||||||
PFNGLSHADERSOURCEPROC glShaderSource = NULL;
|
|
||||||
PFNGLCOMPILESHADERPROC glCompileShader = NULL;
|
|
||||||
PFNGLATTACHSHADERPROC glAttachShader = NULL;
|
|
||||||
PFNGLDETACHSHADERPROC glDetachShader = NULL;
|
|
||||||
PFNGLGETSHADERIVPROC glGetShaderiv = NULL;
|
|
||||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = NULL;
|
|
||||||
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation = NULL;
|
|
||||||
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = NULL;
|
|
||||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = NULL;
|
|
||||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray = NULL;
|
|
||||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = NULL;
|
|
||||||
PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv = NULL;
|
|
||||||
PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = NULL;
|
|
||||||
PFNGLUNIFORM1IPROC glUniform1i = NULL;
|
|
||||||
PFNGLUNIFORM1FVPROC glUniform1fv = NULL;
|
|
||||||
PFNGLUNIFORM3FVPROC glUniform2fv = NULL;
|
|
||||||
PFNGLUNIFORM3FVPROC glUniform3fv = NULL;
|
|
||||||
PFNGLUNIFORM4FVPROC glUniform4fv = NULL;
|
|
||||||
PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f = NULL;
|
|
||||||
PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f = NULL;
|
|
||||||
PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f = NULL;
|
|
||||||
PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f = NULL;
|
|
||||||
PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv = NULL;
|
|
||||||
PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv = NULL;
|
|
||||||
PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv = NULL;
|
|
||||||
PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib = NULL;
|
|
||||||
PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
//=======================================
|
|
||||||
//=========== Multitexture ==============
|
|
||||||
//=======================================
|
|
||||||
|
|
||||||
//Requires GL version 1.3
|
|
||||||
PFNGLACTIVETEXTUREPROC glActiveTexture = NULL;
|
|
||||||
|
|
||||||
//=======================================
|
|
||||||
//========== Vertex buffer ==============
|
|
||||||
//=======================================
|
|
||||||
|
|
||||||
//Requires GL_VERSION_1_5
|
|
||||||
PFNGLGENBUFFERSPROC glGenBuffers = NULL;
|
|
||||||
PFNGLDELETEBUFFERSPROC glDeleteBuffers = NULL;
|
|
||||||
PFNGLBINDBUFFERPROC glBindBuffer = NULL;
|
|
||||||
PFNGLBUFFERDATAPROC glBufferData = NULL;
|
|
||||||
PFNGLBUFFERSUBDATAPROC glBufferSubData = NULL;
|
|
||||||
PFNGLMAPBUFFERPROC glMapBuffer = NULL;
|
|
||||||
PFNGLUNMAPBUFFERPROC glUnmapBuffer = NULL;
|
|
||||||
|
|
||||||
//=========================================
|
|
||||||
//============ Frame buffer ===============
|
|
||||||
//=========================================
|
|
||||||
|
|
||||||
//Requires GL_ARB_framebuffer_object
|
|
||||||
PFNGLISRENDERBUFFERPROC glIsRenderbuffer = NULL;
|
|
||||||
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer = NULL;
|
|
||||||
PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers = NULL;
|
|
||||||
PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers = NULL;
|
|
||||||
PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage = NULL;
|
|
||||||
PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv = NULL;
|
|
||||||
PFNGLISFRAMEBUFFERPROC glIsFramebuffer = NULL;
|
|
||||||
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer = NULL;
|
|
||||||
PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = NULL;
|
|
||||||
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
|
|
||||||
PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus = NULL;
|
|
||||||
PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D = NULL;
|
|
||||||
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = NULL;
|
|
||||||
PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D = NULL;
|
|
||||||
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer = NULL;
|
|
||||||
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv = NULL;
|
|
||||||
PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL;
|
|
||||||
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample = NULL;
|
|
||||||
PFNGLGENERATEMIPMAPPROC glGenerateMipmap = NULL;
|
|
||||||
PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========================================
|
|
||||||
//============ Uniform buffer ===============
|
|
||||||
//===========================================
|
|
||||||
|
|
||||||
//Requires GL_ARB_uniform_buffer_object
|
|
||||||
PFNGLGETUNIFORMINDICESPROC glGetUniformIndices = NULL;
|
|
||||||
PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv = NULL;
|
|
||||||
PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName = NULL;
|
|
||||||
PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex = NULL;
|
|
||||||
PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv = NULL;
|
|
||||||
PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName = NULL;
|
|
||||||
PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding = NULL;
|
|
||||||
PFNGLBINDBUFFERBASEPROC glBindBufferBase = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = NULL;
|
|
||||||
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = NULL;
|
|
||||||
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray = NULL;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ZL {
|
|
||||||
|
|
||||||
bool BindOpenGlFunctions()
|
|
||||||
{
|
|
||||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
|
||||||
//char* extensionList = (char*)glGetString(GL_EXTENSIONS);
|
|
||||||
char* glVersion = (char*)glGetString(GL_VERSION);
|
|
||||||
bool ok = true;
|
|
||||||
|
|
||||||
//Requires OpenGL 2.0 or above
|
|
||||||
if (glVersion[0] >= '2')
|
|
||||||
{
|
|
||||||
|
|
||||||
glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture");
|
|
||||||
|
|
||||||
glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
|
|
||||||
glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)wglGetProcAddress("glDeleteBuffers");
|
|
||||||
glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
|
|
||||||
glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
|
|
||||||
glBufferSubData = (PFNGLBUFFERSUBDATAPROC)wglGetProcAddress("glBufferSubData");
|
|
||||||
glMapBuffer = (PFNGLMAPBUFFERPROC)wglGetProcAddress("glMapBuffer");
|
|
||||||
glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)wglGetProcAddress("glUnmapBuffer");
|
|
||||||
|
|
||||||
glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
|
|
||||||
glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram");
|
|
||||||
glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
|
|
||||||
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)wglGetProcAddress("glValidateProgram");
|
|
||||||
glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
|
|
||||||
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
|
|
||||||
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
|
|
||||||
glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
|
|
||||||
|
|
||||||
|
|
||||||
glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
|
|
||||||
glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
|
|
||||||
glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
|
|
||||||
glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
|
|
||||||
glDetachShader = (PFNGLDETACHSHADERPROC)wglGetProcAddress("glDetachShader");
|
|
||||||
glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
|
|
||||||
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
|
|
||||||
glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)wglGetProcAddress("glGetAttribLocation");
|
|
||||||
glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");
|
|
||||||
glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
|
|
||||||
|
|
||||||
glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glDisableVertexAttribArray");
|
|
||||||
glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation");
|
|
||||||
glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)wglGetProcAddress("glUniformMatrix3fv");
|
|
||||||
glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv");
|
|
||||||
glUniform1i = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i");
|
|
||||||
glUniform1fv = (PFNGLUNIFORM1FVPROC)wglGetProcAddress("glUniform1fv");
|
|
||||||
glUniform2fv = (PFNGLUNIFORM2FVPROC)wglGetProcAddress("glUniform2fv");
|
|
||||||
glUniform3fv = (PFNGLUNIFORM3FVPROC)wglGetProcAddress("glUniform3fv");
|
|
||||||
glUniform4fv = (PFNGLUNIFORM4FVPROC)wglGetProcAddress("glUniform4fv");
|
|
||||||
|
|
||||||
glVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC)wglGetProcAddress("glVertexAttrib1f");
|
|
||||||
glVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC)wglGetProcAddress("glVertexAttrib2f");
|
|
||||||
glVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC)wglGetProcAddress("glVertexAttrib3f");
|
|
||||||
glVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC)wglGetProcAddress("glVertexAttrib4f");
|
|
||||||
glVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC)wglGetProcAddress("glVertexAttrib2fv");
|
|
||||||
glVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC)wglGetProcAddress("glVertexAttrib3fv");
|
|
||||||
glVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC)wglGetProcAddress("glVertexAttrib4fv");
|
|
||||||
glGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC)wglGetProcAddress("glGetActiveAttrib");
|
|
||||||
glGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC)wglGetProcAddress("glGetActiveUniform");
|
|
||||||
|
|
||||||
|
|
||||||
if (glActiveTexture == NULL ||
|
|
||||||
glGenBuffers == NULL ||
|
|
||||||
glDeleteBuffers == NULL ||
|
|
||||||
glBindBuffer == NULL ||
|
|
||||||
glBufferData == NULL ||
|
|
||||||
glBufferSubData == NULL ||
|
|
||||||
glMapBuffer == NULL ||
|
|
||||||
glCreateProgram == NULL ||
|
|
||||||
glDeleteProgram == NULL ||
|
|
||||||
glLinkProgram == NULL ||
|
|
||||||
glValidateProgram == NULL ||
|
|
||||||
glUseProgram == NULL ||
|
|
||||||
glGetProgramiv == NULL ||
|
|
||||||
glGetProgramInfoLog == NULL ||
|
|
||||||
glCreateShader == NULL ||
|
|
||||||
glDeleteShader == NULL ||
|
|
||||||
glShaderSource == NULL ||
|
|
||||||
glCompileShader == NULL ||
|
|
||||||
glAttachShader == NULL ||
|
|
||||||
glDetachShader == NULL ||
|
|
||||||
glGetShaderiv == NULL ||
|
|
||||||
glGetShaderInfoLog == NULL ||
|
|
||||||
glGetAttribLocation == NULL ||
|
|
||||||
glVertexAttribPointer == NULL ||
|
|
||||||
glEnableVertexAttribArray == NULL ||
|
|
||||||
glDisableVertexAttribArray == NULL ||
|
|
||||||
glGetUniformLocation == NULL ||
|
|
||||||
glUniformMatrix3fv == NULL ||
|
|
||||||
glUniformMatrix4fv == NULL ||
|
|
||||||
glUniform1i == NULL ||
|
|
||||||
glUniform1fv == NULL ||
|
|
||||||
glUniform2fv == NULL ||
|
|
||||||
glUniform3fv == NULL ||
|
|
||||||
glUniform4fv == NULL ||
|
|
||||||
glEnableVertexAttribArray == NULL ||
|
|
||||||
glVertexAttrib1f == NULL ||
|
|
||||||
glVertexAttrib2f == NULL ||
|
|
||||||
glVertexAttrib3f == NULL ||
|
|
||||||
glVertexAttrib4f == NULL ||
|
|
||||||
glVertexAttrib2fv == NULL ||
|
|
||||||
glVertexAttrib3fv == NULL ||
|
|
||||||
glVertexAttrib4fv == NULL ||
|
|
||||||
glGetActiveAttrib == NULL ||
|
|
||||||
glGetActiveUniform == NULL)
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)wglGetProcAddress("glIsRenderbuffer");
|
|
||||||
glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)wglGetProcAddress("glBindRenderbuffer");
|
|
||||||
glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)wglGetProcAddress("glDeleteRenderbuffers");
|
|
||||||
glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)wglGetProcAddress("glGenRenderbuffers");
|
|
||||||
glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)wglGetProcAddress("glRenderbufferStorage");
|
|
||||||
glGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC)wglGetProcAddress("glGetRenderbufferParameteriv");
|
|
||||||
glIsFramebuffer = (PFNGLISFRAMEBUFFERPROC)wglGetProcAddress("glIsFramebuffer");
|
|
||||||
glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)wglGetProcAddress("glBindFramebuffer");
|
|
||||||
glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)wglGetProcAddress("glDeleteFramebuffers");
|
|
||||||
glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)wglGetProcAddress("glGenFramebuffers");
|
|
||||||
glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)wglGetProcAddress("glCheckFramebufferStatus");
|
|
||||||
glFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC)wglGetProcAddress("glFramebufferTexture1D");
|
|
||||||
glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)wglGetProcAddress("glFramebufferTexture2D");
|
|
||||||
glFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC)wglGetProcAddress("glFramebufferTexture3D");
|
|
||||||
glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)wglGetProcAddress("glFramebufferRenderbuffer");
|
|
||||||
glGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC)wglGetProcAddress("glGetFramebufferAttachmentParameteriv");
|
|
||||||
glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)wglGetProcAddress("glBlitFramebuffer");
|
|
||||||
glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)wglGetProcAddress("glRenderbufferStorageMultisample");
|
|
||||||
glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmap");
|
|
||||||
glFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC)wglGetProcAddress("glFramebufferTextureLayer");
|
|
||||||
|
|
||||||
if (glIsRenderbuffer == NULL ||
|
|
||||||
glBindRenderbuffer == NULL ||
|
|
||||||
glDeleteRenderbuffers == NULL ||
|
|
||||||
glGenRenderbuffers == NULL ||
|
|
||||||
glRenderbufferStorage == NULL ||
|
|
||||||
glGetRenderbufferParameteriv == NULL ||
|
|
||||||
glIsFramebuffer == NULL ||
|
|
||||||
glBindFramebuffer == NULL ||
|
|
||||||
glDeleteFramebuffers == NULL ||
|
|
||||||
glGenFramebuffers == NULL ||
|
|
||||||
glCheckFramebufferStatus == NULL ||
|
|
||||||
glFramebufferTexture1D == NULL ||
|
|
||||||
glFramebufferTexture2D == NULL ||
|
|
||||||
glFramebufferTexture3D == NULL ||
|
|
||||||
glFramebufferRenderbuffer == NULL ||
|
|
||||||
glGetFramebufferAttachmentParameteriv == NULL ||
|
|
||||||
glBlitFramebuffer == NULL ||
|
|
||||||
glRenderbufferStorageMultisample == NULL ||
|
|
||||||
glGenerateMipmap == NULL ||
|
|
||||||
glFramebufferTextureLayer == NULL)
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)wglGetProcAddress("glGetUniformIndices");
|
|
||||||
glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)wglGetProcAddress("glGetActiveUniformsiv");
|
|
||||||
glGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC)wglGetProcAddress("glGetActiveUniformName");
|
|
||||||
glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)wglGetProcAddress("glGetUniformBlockIndex");
|
|
||||||
glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)wglGetProcAddress("glGetActiveUniformBlockiv");
|
|
||||||
glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC)wglGetProcAddress("glGetActiveUniformBlockName");
|
|
||||||
glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)wglGetProcAddress("glUniformBlockBinding");
|
|
||||||
glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)wglGetProcAddress("glBindBufferBase");
|
|
||||||
|
|
||||||
if (glGetUniformIndices == NULL ||
|
|
||||||
glGetActiveUniformsiv == NULL ||
|
|
||||||
glGetActiveUniformName == NULL ||
|
|
||||||
glGetUniformBlockIndex == NULL ||
|
|
||||||
glGetActiveUniformBlockiv == NULL ||
|
|
||||||
glGetActiveUniformBlockName == NULL ||
|
|
||||||
glUniformBlockBinding == NULL ||
|
|
||||||
glBindBufferBase == NULL)
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays");
|
|
||||||
glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray");
|
|
||||||
glDeleteVertexArray = (PFNGLDELETEVERTEXARRAYSPROC)wglGetProcAddress("glBindVertexArray");
|
|
||||||
|
|
||||||
if (glGenVertexArrays == NULL ||
|
|
||||||
glBindVertexArray == NULL ||
|
|
||||||
glDeleteVertexArray == NULL)
|
|
||||||
{
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
#else
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckGlError()
|
|
||||||
{
|
|
||||||
size_t error = glGetError();
|
|
||||||
if (error != GL_NO_ERROR)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Gl error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
|
|
||||||
#include "SDL.h"
|
|
||||||
#ifdef EMSCRIPTEN
|
|
||||||
//#define GL_GLEXT_PROTOTYPES 1
|
|
||||||
//#define EGL_EGLEXT_PROTOTYPES 1
|
|
||||||
//#include <SDL2/SDL_opengl.h>
|
|
||||||
#include <GLES3/gl3.h>
|
|
||||||
#include "emscripten.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
#include <GL/gl.h>
|
|
||||||
#include <GL/glu.h>
|
|
||||||
#include <GLES3/gl3.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <stdexcept>
|
|
||||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
|
|
||||||
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
|
|
||||||
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
|
|
||||||
|
|
||||||
//#define GL_GLEXT_PROTOTYPES
|
|
||||||
|
|
||||||
#include "gl/gl.h"
|
|
||||||
#include "gl/glu.h"
|
|
||||||
#include "gl/glext.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include <array>
|
|
||||||
#include <stack>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <map>
|
|
||||||
#define _USE_MATH_DEFINES
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
//Requires GL_VERSION_2_0
|
|
||||||
extern PFNGLCREATEPROGRAMPROC glCreateProgram;
|
|
||||||
extern PFNGLDELETEPROGRAMPROC glDeleteProgram;
|
|
||||||
extern PFNGLLINKPROGRAMPROC glLinkProgram;
|
|
||||||
extern PFNGLVALIDATEPROGRAMPROC glValidateProgram;
|
|
||||||
extern PFNGLUSEPROGRAMPROC glUseProgram;
|
|
||||||
extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
|
|
||||||
extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
|
|
||||||
extern PFNGLCREATESHADERPROC glCreateShader;
|
|
||||||
extern PFNGLDELETESHADERPROC glDeleteShader;
|
|
||||||
extern PFNGLSHADERSOURCEPROC glShaderSource;
|
|
||||||
extern PFNGLCOMPILESHADERPROC glCompileShader;
|
|
||||||
extern PFNGLATTACHSHADERPROC glAttachShader;
|
|
||||||
extern PFNGLDETACHSHADERPROC glDetachShader;
|
|
||||||
extern PFNGLGETSHADERIVPROC glGetShaderiv;
|
|
||||||
extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
|
|
||||||
extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
|
|
||||||
extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
|
|
||||||
extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
|
|
||||||
extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
|
|
||||||
extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
|
|
||||||
extern PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv;
|
|
||||||
extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
|
|
||||||
extern PFNGLUNIFORM1IPROC glUniform1i;
|
|
||||||
extern PFNGLUNIFORM1FVPROC glUniform1fv;
|
|
||||||
extern PFNGLUNIFORM3FVPROC glUniform2fv;
|
|
||||||
extern PFNGLUNIFORM3FVPROC glUniform3fv;
|
|
||||||
extern PFNGLUNIFORM4FVPROC glUniform4fv;
|
|
||||||
extern PFNGLVERTEXATTRIB1FPROC glVertexAttrib1f;
|
|
||||||
extern PFNGLVERTEXATTRIB2FPROC glVertexAttrib2f;
|
|
||||||
extern PFNGLVERTEXATTRIB3FPROC glVertexAttrib3f;
|
|
||||||
extern PFNGLVERTEXATTRIB4FPROC glVertexAttrib4f;
|
|
||||||
extern PFNGLVERTEXATTRIB2FVPROC glVertexAttrib2fv;
|
|
||||||
extern PFNGLVERTEXATTRIB3FVPROC glVertexAttrib3fv;
|
|
||||||
extern PFNGLVERTEXATTRIB4FVPROC glVertexAttrib4fv;
|
|
||||||
extern PFNGLGETACTIVEATTRIBPROC glGetActiveAttrib;
|
|
||||||
extern PFNGLGETACTIVEUNIFORMPROC glGetActiveUniform;
|
|
||||||
|
|
||||||
|
|
||||||
//=======================================
|
|
||||||
//=========== Multitexture ==============
|
|
||||||
//=======================================
|
|
||||||
|
|
||||||
//Requires GL version 1.3
|
|
||||||
extern PFNGLACTIVETEXTUREPROC glActiveTexture;
|
|
||||||
|
|
||||||
//=======================================
|
|
||||||
//========== Vertex buffer ==============
|
|
||||||
//=======================================
|
|
||||||
|
|
||||||
//Requires GL_VERSION_1_5
|
|
||||||
extern PFNGLGENBUFFERSPROC glGenBuffers;
|
|
||||||
extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
|
|
||||||
extern PFNGLBINDBUFFERPROC glBindBuffer;
|
|
||||||
extern PFNGLBUFFERDATAPROC glBufferData;
|
|
||||||
extern PFNGLBUFFERSUBDATAPROC glBufferSubData;
|
|
||||||
extern PFNGLMAPBUFFERPROC glMapBuffer;
|
|
||||||
extern PFNGLUNMAPBUFFERPROC glUnmapBuffer;
|
|
||||||
|
|
||||||
//=========================================
|
|
||||||
//============ Frame buffer ===============
|
|
||||||
//=========================================
|
|
||||||
|
|
||||||
//Requires GL_ARB_framebuffer_object
|
|
||||||
extern PFNGLISRENDERBUFFERPROC glIsRenderbuffer;
|
|
||||||
extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
|
|
||||||
extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers;
|
|
||||||
extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
|
|
||||||
extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
|
|
||||||
extern PFNGLGETRENDERBUFFERPARAMETERIVPROC glGetRenderbufferParameteriv;
|
|
||||||
extern PFNGLISFRAMEBUFFERPROC glIsFramebuffer;
|
|
||||||
extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
|
|
||||||
extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
|
|
||||||
extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
|
|
||||||
extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
|
|
||||||
extern PFNGLFRAMEBUFFERTEXTURE1DPROC glFramebufferTexture1D;
|
|
||||||
extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
|
|
||||||
extern PFNGLFRAMEBUFFERTEXTURE3DPROC glFramebufferTexture3D;
|
|
||||||
extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
|
|
||||||
extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glGetFramebufferAttachmentParameteriv;
|
|
||||||
extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
|
|
||||||
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample;
|
|
||||||
extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
|
|
||||||
extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===========================================
|
|
||||||
//============ Uniform buffer ===============
|
|
||||||
//===========================================
|
|
||||||
|
|
||||||
//Requires GL_ARB_uniform_buffer_object
|
|
||||||
extern PFNGLGETUNIFORMINDICESPROC glGetUniformIndices;
|
|
||||||
extern PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv;
|
|
||||||
extern PFNGLGETACTIVEUNIFORMNAMEPROC glGetActiveUniformName;
|
|
||||||
extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
|
|
||||||
extern PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv;
|
|
||||||
extern PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC glGetActiveUniformBlockName;
|
|
||||||
extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
|
|
||||||
extern PFNGLBINDBUFFERBASEPROC glBindBufferBase;
|
|
||||||
|
|
||||||
|
|
||||||
extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
|
|
||||||
extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
|
|
||||||
extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray;
|
|
||||||
#else
|
|
||||||
|
|
||||||
#endif
|
|
||||||
namespace ZL {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool BindOpenGlFunctions();
|
|
||||||
|
|
||||||
void CheckGlError();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
### Перед запуском в папке ```app/jni/```(рядом с src) нужно создать три папки с исходниками библиотек ```libpng```, ```SDL```, ```zlib```
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
# Script to run:
|
|
||||||
|
|
||||||
```
|
|
||||||
C:\Work\Projects\emsdk\emsdk.bat activate latest
|
|
||||||
C:\Work\Projects\emsdk\emsdk_env.bat
|
|
||||||
emcc main.cpp Game.cpp Math.cpp Physics.cpp Renderer.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -sTOTAL_MEMORY=33554432 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS="[""png""]" -sUSE_SDL=2 --preload-file background.bmp --preload-file bird.bmp32 --preload-file default.fragment --preload-file default.vertex --preload-file game_over.bmp32 --preload-file pipe.bmp32 -o jumpingbird.html
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
zlib-1.3.1:
|
|
||||||
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=install ..
|
|
||||||
|
|
||||||
then run ALL_BUILD and INSTALL in Visual Studio
|
|
||||||
|
|
||||||
lpng1645:
|
|
||||||
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake -DCMAKE_INSTALL_PREFIX=install -DZLIB_ROOT=C:\Work\Projects\zlib-1.3.1\build\install ..
|
|
||||||
|
|
||||||
then run ALL_BUILD and INSTALL in Visual Studio
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
https://github.com/Bly7/OBJ-Loader/blob/master/Source/OBJ_Loader.h
|
|
||||||
|
|
||||||
|
|
||||||
https://github.com/gametutorials/tutorials/blob/master/OpenGL/MD3%20Animation/Main.cpp
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
linux:
|
|
||||||
```
|
|
||||||
g++ Game.cpp main.cpp Math.cpp OpenGlExtensions.cpp Physics.cpp Renderer.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp BoneAnimatedModel.cpp ObjLoader.cpp cmakeaudioplayer/src/AudioPlayer.cpp TextModel.cpp Inventory.cpp -o sdl_app -O2 -std=c++17 \
|
|
||||||
-I cmakeaudioplayer/include \
|
|
||||||
$(pkg-config --cflags --libs sdl2 gl) \
|
|
||||||
$(pkg-config --cflags --libs vorbis vorbisfile ogg) \
|
|
||||||
-lopenal
|
|
||||||
```
|
|
||||||
|
|
||||||
# Emscripten new
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
cd build-emcmake/
|
|
||||||
|
|
||||||
emcmake cmake -DCMAKE_INSTALL_PREFIX=install ..
|
|
||||||
cmake --build .
|
|
||||||
|
|
||||||
cmake --install .
|
|
||||||
|
|
||||||
|
|
||||||
emcc main.cpp Game.cpp Environment.cpp GameObjectManager.cpp BoneAnimatedModel.cpp GameWorld.cpp InputManager.cpp Inventory.cpp ObjLoader.cpp QuestScripts.cpp RenderSystem.cpp Math.cpp Physics.cpp Renderer.cpp TextModel.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -IC:\Users\ASUS\Desktop\fishrungame2\ZeptoLabTest1\thirdparty\libzip-1.11.3\build-emcmake\install\include -LC:\Users\ASUS\Desktop\fishrungame2\ZeptoLabTest1\thirdparty\libzip-1.11.3\build-emcmake\install\lib -lzip -sTOTAL_MEMORY=33554432 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS="[""png""]" -sUSE_SDL=2 --preload-file background.bmp --preload-file bird.bmp32 --preload-file default.fragment --preload-file default.vertex --preload-file game_over.bmp32 --preload-file pipe.bmp32 -o jumpingbird.html
|
|
||||||
|
|
||||||
emcc main.cpp Game.cpp Environment.cpp GameObjectManager.cpp BoneAnimatedModel.cpp GameWorld.cpp InputManager.cpp Inventory.cpp ObjLoader.cpp QuestScripts.cpp RenderSystem.cpp Math.cpp Physics.cpp Renderer.cpp TextModel.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -I./thirdparty/zlib-1.3.1/install/include -L./thirdparty/libzip-1.11.3/build-emcmake/install/lib -L./thirdparty/zlib-1.3.1/install/lib -lzip -lz -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS='["png"]' -sUSE_SDL=2 --preload-file data.zip -o jumpingbird.html
|
|
||||||
|
|
||||||
|
|
||||||
# Windows:
|
|
||||||
emcc main.cpp Game.cpp Environment.cpp GameObjectManager.cpp BoneAnimatedModel.cpp GameWorld.cpp InputManager.cpp Inventory.cpp ObjLoader.cpp QuestScripts.cpp RenderSystem.cpp Math.cpp Physics.cpp Renderer.cpp TextModel.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -L./thirdparty/libzip-1.11.3/build-emcmake/install/lib -lzip -lz -sUSE_SDL_IMAGE=2 -sUSE_SDL=2 -sUSE_LIBPNG=1 --preload-file data.zip -o viola.html
|
|
||||||
|
|
||||||
emrun --no_browser --port 8080 .
|
|
||||||
```
|
|
||||||
|
|
||||||
# License
|
|
||||||
Code: MIT
|
|
||||||
|
|
||||||
Art: CC-BY
|
|
||||||
|
|
||||||
# Cmake Run Linux
|
|
||||||
Run using cmakelist
|
|
||||||
make -j$(nproc) -C build #Компилируем
|
|
||||||
./build/sdl_app #Запускаем
|
|
||||||
|
|
||||||
Для постройки без звука
|
|
||||||
rm -rf build #Очищаем build папку
|
|
||||||
cmake -B build -DAUDIO=1 #Пересоздаём конфигурацию CMake
|
|
||||||
@@ -1,212 +0,0 @@
|
|||||||
#include "ShaderManager.h"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
|
|
||||||
namespace ZL {
|
|
||||||
|
|
||||||
ShaderResource::ShaderResource(const std::string& vertexCode, const std::string& fragmentCode)
|
|
||||||
{
|
|
||||||
|
|
||||||
const int CONST_INFOLOG_LENGTH = 256;
|
|
||||||
|
|
||||||
char infoLog[CONST_INFOLOG_LENGTH];
|
|
||||||
int infoLogLength;
|
|
||||||
|
|
||||||
int vertexShaderCompiled;
|
|
||||||
int fragmentShaderCompiled;
|
|
||||||
int programLinked;
|
|
||||||
|
|
||||||
GLuint vertexShader;
|
|
||||||
GLuint fragmentShader;
|
|
||||||
|
|
||||||
int vertexCodeLength = static_cast<int>(strlen(vertexCode.c_str()));
|
|
||||||
int fragmentCodeLength = static_cast<int>(strlen(fragmentCode.c_str()));
|
|
||||||
|
|
||||||
const char* vc = &vertexCode[0];
|
|
||||||
const char* fc = &fragmentCode[0];
|
|
||||||
|
|
||||||
vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
|
||||||
glShaderSource(vertexShader, 1, &(vc), &vertexCodeLength);
|
|
||||||
|
|
||||||
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
||||||
glShaderSource(fragmentShader, 1, &(fc), &fragmentCodeLength);
|
|
||||||
|
|
||||||
glCompileShader(vertexShader);
|
|
||||||
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexShaderCompiled);
|
|
||||||
glGetShaderInfoLog(vertexShader, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
|
||||||
|
|
||||||
glCompileShader(fragmentShader);
|
|
||||||
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentShaderCompiled);
|
|
||||||
glGetShaderInfoLog(fragmentShader, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
|
||||||
|
|
||||||
if (!vertexShaderCompiled)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to compile vertex shader code!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fragmentShaderCompiled)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to compile fragment shader code!");
|
|
||||||
}
|
|
||||||
|
|
||||||
shaderProgram = glCreateProgram();
|
|
||||||
|
|
||||||
glAttachShader(shaderProgram, vertexShader);
|
|
||||||
glAttachShader(shaderProgram, fragmentShader);
|
|
||||||
|
|
||||||
glLinkProgram(shaderProgram);
|
|
||||||
|
|
||||||
glDeleteShader(vertexShader);
|
|
||||||
glDeleteShader(fragmentShader);
|
|
||||||
|
|
||||||
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &programLinked);
|
|
||||||
glGetProgramInfoLog(shaderProgram, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
|
||||||
|
|
||||||
if (!programLinked)
|
|
||||||
{
|
|
||||||
shaderProgram = 0;
|
|
||||||
throw std::runtime_error("Failed to link shader program!");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int dummySize; //Dummy
|
|
||||||
int dummyLen; //Dummy
|
|
||||||
GLenum dummyEnum;
|
|
||||||
|
|
||||||
|
|
||||||
//================= Parsing all uniforms ================
|
|
||||||
|
|
||||||
int activeUniforms;
|
|
||||||
|
|
||||||
const int CONST_UNIFORM_NAME_LENGTH = 256;
|
|
||||||
char uniformName[CONST_UNIFORM_NAME_LENGTH];
|
|
||||||
|
|
||||||
glGetProgramiv(shaderProgram, GL_ACTIVE_UNIFORMS, &activeUniforms);
|
|
||||||
|
|
||||||
for (int i = 0; i < activeUniforms; i++)
|
|
||||||
{
|
|
||||||
glGetActiveUniform(shaderProgram, i, CONST_UNIFORM_NAME_LENGTH, &dummyLen, &dummySize, &dummyEnum, uniformName);
|
|
||||||
|
|
||||||
uniformList[uniformName] = glGetUniformLocation(shaderProgram, uniformName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//================= Parsing all attributes ================
|
|
||||||
int activeAttribs;
|
|
||||||
|
|
||||||
const int CONST_ATTRIB_NAME_LENGTH = 256;
|
|
||||||
char attribName[CONST_ATTRIB_NAME_LENGTH];
|
|
||||||
|
|
||||||
glGetProgramiv(shaderProgram, GL_ACTIVE_ATTRIBUTES, &activeAttribs);
|
|
||||||
|
|
||||||
for (int i = 0; i < activeAttribs; i++)
|
|
||||||
{
|
|
||||||
glGetActiveAttrib(shaderProgram, i, CONST_ATTRIB_NAME_LENGTH, &dummyLen, &dummySize, &dummyEnum, attribName);
|
|
||||||
attribList[attribName] = glGetAttribLocation(shaderProgram, attribName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderResource::~ShaderResource()
|
|
||||||
{
|
|
||||||
if (shaderProgram != 0)
|
|
||||||
{
|
|
||||||
glDeleteProgram(shaderProgram);
|
|
||||||
|
|
||||||
shaderProgram = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GLuint ShaderResource::getShaderProgram()
|
|
||||||
{
|
|
||||||
return shaderProgram;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ShaderManager::AddShaderFromFiles(const std::string& shaderName, const std::string& vertexShaderFileName, const std::string& fragmentShaderFileName, const std::string& ZIPFileName)
|
|
||||||
{
|
|
||||||
|
|
||||||
std::string vertexShader;
|
|
||||||
std::string fragmentShader;
|
|
||||||
|
|
||||||
if (!ZIPFileName.empty()){
|
|
||||||
|
|
||||||
std::vector<char> vertexShaderData;
|
|
||||||
std::vector<char> fragmentShaderData;
|
|
||||||
|
|
||||||
vertexShaderData = readFileFromZIP(vertexShaderFileName, ZIPFileName);
|
|
||||||
fragmentShaderData = readFileFromZIP(fragmentShaderFileName, ZIPFileName);
|
|
||||||
|
|
||||||
vertexShader = std::string(vertexShaderData.begin(), vertexShaderData.end());
|
|
||||||
fragmentShader = std::string(fragmentShaderData.begin(), fragmentShaderData.end());
|
|
||||||
|
|
||||||
}else{
|
|
||||||
vertexShader = readTextFile(vertexShaderFileName);
|
|
||||||
fragmentShader = readTextFile(fragmentShaderFileName);
|
|
||||||
}
|
|
||||||
///std::cout << "Shader: "<< vertexShader << std::endl;
|
|
||||||
shaderResourceMap[shaderName] = std::make_shared<ShaderResource>(vertexShader, fragmentShader);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShaderManager::PushShader(const std::string& shaderName)
|
|
||||||
{
|
|
||||||
if (shaderStack.size() >= CONST_MAX_SHADER_STACK_SIZE)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Shader stack overflow!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shaderResourceMap.find(shaderName) == shaderResourceMap.end())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Shader does not exist!");
|
|
||||||
}
|
|
||||||
|
|
||||||
shaderStack.push(shaderName);
|
|
||||||
|
|
||||||
glUseProgram(shaderResourceMap[shaderName]->getShaderProgram());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ShaderManager::PopShader()
|
|
||||||
{
|
|
||||||
if (shaderStack.size() == 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Shader stack underflow!");
|
|
||||||
}
|
|
||||||
|
|
||||||
shaderStack.pop();
|
|
||||||
|
|
||||||
if (shaderStack.size() == 0)
|
|
||||||
{
|
|
||||||
glUseProgram(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
glUseProgram(shaderResourceMap[shaderStack.top()]->getShaderProgram());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ShaderResource> ShaderManager::GetCurrentShader()
|
|
||||||
{
|
|
||||||
if (shaderStack.size() == 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Shader stack underflow!");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return shaderResourceMap[shaderStack.top()];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ShaderSetter::ShaderSetter(ShaderManager& inShaderManager, const std::string& shaderName)
|
|
||||||
: shaderManager(shaderManager)
|
|
||||||
{
|
|
||||||
shaderManager.PushShader(shaderName);
|
|
||||||
}
|
|
||||||
|
|
||||||
ShaderSetter::~ShaderSetter()
|
|
||||||
{
|
|
||||||
shaderManager.PopShader();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-390
@@ -1,390 +0,0 @@
|
|||||||
#include "TextModel.h"
|
|
||||||
#include <regex>
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace ZL
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
VertexDataStruct LoadFromTextFile(const std::string& fileName, const std::string& ZIPFileName)
|
|
||||||
{
|
|
||||||
VertexDataStruct result;
|
|
||||||
std::ifstream filestream;
|
|
||||||
std::istringstream zipStream;
|
|
||||||
|
|
||||||
if (!ZIPFileName.empty())
|
|
||||||
{
|
|
||||||
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
|
||||||
std::string fileContents(fileData.begin(), fileData.end());
|
|
||||||
zipStream.str(fileContents);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filestream.open(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Создаем ссылку f на нужный поток – после этого код ниже остается без изменений
|
|
||||||
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
|
|
||||||
|
|
||||||
|
|
||||||
//Skip first 5 lines
|
|
||||||
std::string tempLine;
|
|
||||||
|
|
||||||
std::getline(f, tempLine);
|
|
||||||
|
|
||||||
static const std::regex pattern_count(R"(\d+)");
|
|
||||||
static const std::regex pattern_float(R"([-]?\d+\.\d+)");
|
|
||||||
static const std::regex pattern_int(R"([-]?\d+)");
|
|
||||||
|
|
||||||
|
|
||||||
std::smatch match;
|
|
||||||
|
|
||||||
int numberVertices;
|
|
||||||
|
|
||||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
|
||||||
std::string number_str = match.str();
|
|
||||||
numberVertices = std::stoi(number_str);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw std::runtime_error("No number found in the input string.");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Vector3f> vertices;
|
|
||||||
|
|
||||||
vertices.resize(numberVertices);
|
|
||||||
for (int i = 0; i < numberVertices; i++)
|
|
||||||
{
|
|
||||||
std::getline(f, tempLine);
|
|
||||||
|
|
||||||
std::vector<float> floatValues;
|
|
||||||
|
|
||||||
auto b = tempLine.cbegin();
|
|
||||||
auto e = tempLine.cend();
|
|
||||||
while (std::regex_search(b, e, match, pattern_float)) {
|
|
||||||
floatValues.push_back(std::stof(match.str()));
|
|
||||||
b = match.suffix().first;
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "UV Coordinates" << std::endl;
|
|
||||||
|
|
||||||
std::getline(f, tempLine); //===UV Coordinates:
|
|
||||||
|
|
||||||
std::getline(f, tempLine); //triangle count
|
|
||||||
int numberTriangles;
|
|
||||||
|
|
||||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
|
||||||
std::string number_str = match.str();
|
|
||||||
numberTriangles = std::stoi(number_str);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw std::runtime_error("No number found in the input string.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Now process UVs
|
|
||||||
std::vector<std::array<Vector2f, 3>> uvCoords;
|
|
||||||
|
|
||||||
uvCoords.resize(numberTriangles);
|
|
||||||
|
|
||||||
for (int i = 0; i < numberTriangles; i++)
|
|
||||||
{
|
|
||||||
std::getline(f, tempLine); //Face 0
|
|
||||||
|
|
||||||
int uvCount;
|
|
||||||
std::getline(f, tempLine);
|
|
||||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
|
||||||
std::string number_str = match.str();
|
|
||||||
uvCount = std::stoi(number_str);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw std::runtime_error("No number found in the input string.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uvCount != 3)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("more than 3 uvs");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<float> floatValues;
|
|
||||||
|
|
||||||
for (int j = 0; j < 3; j++)
|
|
||||||
{
|
|
||||||
std::getline(f, tempLine); //UV <Vector (-0.3661, -1.1665)>
|
|
||||||
|
|
||||||
auto b = tempLine.cbegin();
|
|
||||||
auto e = tempLine.cend();
|
|
||||||
floatValues.clear();
|
|
||||||
while (std::regex_search(b, e, match, pattern_float)) {
|
|
||||||
floatValues.push_back(std::stof(match.str()));
|
|
||||||
b = match.suffix().first;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (floatValues.size() != 2)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("more than 2 uvs---");
|
|
||||||
}
|
|
||||||
|
|
||||||
uvCoords[i][j] = Vector2f{ floatValues[0],floatValues[1] };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Normals go" << std::endl;
|
|
||||||
|
|
||||||
std::getline(f, tempLine); //===Normals:
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<Vector3f> normals;
|
|
||||||
|
|
||||||
normals.resize(numberVertices);
|
|
||||||
for (int i = 0; i < numberVertices; i++)
|
|
||||||
{
|
|
||||||
std::getline(f, tempLine);
|
|
||||||
|
|
||||||
std::vector<float> floatValues;
|
|
||||||
|
|
||||||
auto b = tempLine.cbegin();
|
|
||||||
auto e = tempLine.cend();
|
|
||||||
while (std::regex_search(b, e, match, pattern_float)) {
|
|
||||||
floatValues.push_back(std::stof(match.str()));
|
|
||||||
b = match.suffix().first;
|
|
||||||
}
|
|
||||||
|
|
||||||
normals[i] = Vector3f{ floatValues[0], floatValues[1], floatValues[2] };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Triangles go:" << std::endl;
|
|
||||||
|
|
||||||
std::getline(f, tempLine); //===Triangles: 3974
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<std::array<int, 3>> triangles;
|
|
||||||
|
|
||||||
triangles.resize(numberTriangles);
|
|
||||||
for (int i = 0; i < numberTriangles; i++)
|
|
||||||
{
|
|
||||||
std::getline(f, tempLine);
|
|
||||||
|
|
||||||
std::vector<int> intValues;
|
|
||||||
|
|
||||||
auto b = tempLine.cbegin();
|
|
||||||
auto e = tempLine.cend();
|
|
||||||
while (std::regex_search(b, e, match, pattern_int)) {
|
|
||||||
intValues.push_back(std::stoi(match.str()));
|
|
||||||
b = match.suffix().first;
|
|
||||||
}
|
|
||||||
|
|
||||||
triangles[i] = { intValues[0], intValues[1], intValues[2] };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::cout << "Process vertices" << std::endl;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Now let's process vertices
|
|
||||||
|
|
||||||
for (int i = 0; i < numberTriangles; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
result.PositionData.push_back(vertices[triangles[i][0]]);
|
|
||||||
result.PositionData.push_back(vertices[triangles[i][1]]);
|
|
||||||
result.PositionData.push_back(vertices[triangles[i][2]]);
|
|
||||||
|
|
||||||
/*
|
|
||||||
result.NormalData.push_back(normals[triangles[i][0]]);
|
|
||||||
result.NormalData.push_back(normals[triangles[i][1]]);
|
|
||||||
result.NormalData.push_back(normals[triangles[i][2]]);
|
|
||||||
*/
|
|
||||||
result.TexCoordData.push_back(uvCoords[i][0]);
|
|
||||||
result.TexCoordData.push_back(uvCoords[i][1]);
|
|
||||||
result.TexCoordData.push_back(uvCoords[i][2]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//Swap from Blender format to OpenGL format
|
|
||||||
for (int i = 0; i < result.PositionData.size(); i++)
|
|
||||||
{
|
|
||||||
Vector3f tempVec = result.PositionData[i];
|
|
||||||
result.PositionData[i].v[0] = tempVec.v[1];
|
|
||||||
result.PositionData[i].v[1] = tempVec.v[2];
|
|
||||||
result.PositionData[i].v[2] = tempVec.v[0];
|
|
||||||
|
|
||||||
/*
|
|
||||||
tempVec = result.NormalData[i];
|
|
||||||
result.NormalData[i].v[0] = tempVec.v[1];
|
|
||||||
result.NormalData[i].v[1] = tempVec.v[2];
|
|
||||||
result.NormalData[i].v[2] = tempVec.v[0];*/
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName)
|
|
||||||
{
|
|
||||||
VertexDataStruct result;
|
|
||||||
std::ifstream filestream;
|
|
||||||
std::istringstream zipStream;
|
|
||||||
|
|
||||||
// --- 1. Открытие потока (без изменений) ---
|
|
||||||
if (!ZIPFileName.empty())
|
|
||||||
{
|
|
||||||
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
|
||||||
std::string fileContents(fileData.begin(), fileData.end());
|
|
||||||
zipStream.str(fileContents);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
filestream.open(fileName);
|
|
||||||
if (!filestream.is_open()) {
|
|
||||||
throw std::runtime_error("Failed to open file: " + fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::istream& f = (!ZIPFileName.empty()) ? static_cast<std::istream&>(zipStream) : static_cast<std::istream&>(filestream);
|
|
||||||
|
|
||||||
std::string tempLine;
|
|
||||||
std::smatch match;
|
|
||||||
|
|
||||||
// Обновленные регулярки
|
|
||||||
// pattern_float стал чуть надежнее для чисел вида "0" или "-1" без точки, если вдруг Python округлит до int
|
|
||||||
static const std::regex pattern_count(R"(\d+)");
|
|
||||||
static const std::regex pattern_float(R"([-]?\d+(\.\d+)?)");
|
|
||||||
static const std::regex pattern_int(R"([-]?\d+)");
|
|
||||||
|
|
||||||
// --- 2. Парсинг Вершин (Pos + Norm + UV) ---
|
|
||||||
|
|
||||||
// Ищем заголовок ===Vertices
|
|
||||||
while (std::getline(f, tempLine)) {
|
|
||||||
if (tempLine.find("===Vertices") != std::string::npos) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numberVertices = 0;
|
|
||||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
|
||||||
numberVertices = std::stoi(match.str());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw std::runtime_error("Vertices header not found or invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Временные буферы для хранения "уникальных" вершин перед разверткой по индексам
|
|
||||||
std::vector<Vector3f> tempPositions(numberVertices);
|
|
||||||
std::vector<Vector3f> tempNormals(numberVertices);
|
|
||||||
std::vector<Vector2f> tempUVs(numberVertices);
|
|
||||||
|
|
||||||
for (int i = 0; i < numberVertices; i++)
|
|
||||||
{
|
|
||||||
std::getline(f, tempLine);
|
|
||||||
// Строка вида: V 0: Pos(x, y, z) Norm(x, y, z) UV(u, v)
|
|
||||||
|
|
||||||
std::vector<float> floatValues;
|
|
||||||
floatValues.reserve(8); // Ожидаем ровно 8 чисел (3 pos + 3 norm + 2 uv)
|
|
||||||
|
|
||||||
auto b = tempLine.cbegin();
|
|
||||||
auto e = tempLine.cend();
|
|
||||||
while (std::regex_search(b, e, match, pattern_float)) {
|
|
||||||
floatValues.push_back(std::stof(match.str()));
|
|
||||||
b = match.suffix().first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверка целостности строки (ID вершины regex может поймать первым, но нас интересуют данные)
|
|
||||||
// Обычно ID идет первым (0), потом 3+3+2 float. Итого 9 чисел, если считать ID.
|
|
||||||
// Ваш Python пишет "V 0:", regex поймает 0. Потом 8 флоатов.
|
|
||||||
|
|
||||||
// Если regex ловит ID вершины как float (что вероятно), нам нужно смещение.
|
|
||||||
// ID - floatValues[0]
|
|
||||||
// Pos - [1], [2], [3]
|
|
||||||
// Norm - [4], [5], [6]
|
|
||||||
// UV - [7], [8]
|
|
||||||
|
|
||||||
if (floatValues.size() < 9) {
|
|
||||||
throw std::runtime_error("Malformed vertex line at index " + std::to_string(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
tempPositions[i] = Vector3f{ floatValues[1], floatValues[2], floatValues[3] };
|
|
||||||
tempNormals[i] = Vector3f{ floatValues[4], floatValues[5], floatValues[6] };
|
|
||||||
tempUVs[i] = Vector2f{ floatValues[7], floatValues[8] };
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 3. Парсинг Треугольников (Индексов) ---
|
|
||||||
|
|
||||||
// Пропускаем пустые строки до заголовка треугольников
|
|
||||||
while (std::getline(f, tempLine)) {
|
|
||||||
if (tempLine.find("===Triangles") != std::string::npos) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numberTriangles = 0;
|
|
||||||
if (std::regex_search(tempLine, match, pattern_count)) {
|
|
||||||
numberTriangles = std::stoi(match.str());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw std::runtime_error("Triangles header not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Резервируем память в result, чтобы избежать лишних аллокаций
|
|
||||||
result.PositionData.reserve(numberTriangles * 3);
|
|
||||||
result.NormalData.reserve(numberTriangles * 3);
|
|
||||||
result.TexCoordData.reserve(numberTriangles * 3);
|
|
||||||
|
|
||||||
for (int i = 0; i < numberTriangles; i++)
|
|
||||||
{
|
|
||||||
std::getline(f, tempLine);
|
|
||||||
// Строка вида: Tri: 0 1 2
|
|
||||||
|
|
||||||
std::vector<int> indices;
|
|
||||||
indices.reserve(3);
|
|
||||||
|
|
||||||
auto b = tempLine.cbegin();
|
|
||||||
auto e = tempLine.cend();
|
|
||||||
while (std::regex_search(b, e, match, pattern_int)) {
|
|
||||||
indices.push_back(std::stoi(match.str()));
|
|
||||||
b = match.suffix().first;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indices.size() != 3) {
|
|
||||||
throw std::runtime_error("Malformed triangle line at index " + std::to_string(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 4. Заполнение VertexDataStruct (Flattening) ---
|
|
||||||
// Берем данные из временных буферов по индексам и кладем в итоговый массив
|
|
||||||
|
|
||||||
for (int k = 0; k < 3; k++) {
|
|
||||||
int idx = indices[k];
|
|
||||||
result.PositionData.push_back(tempPositions[idx]);
|
|
||||||
result.NormalData.push_back(tempNormals[idx]);
|
|
||||||
result.TexCoordData.push_back(tempUVs[idx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- 5. Конвертация координат (Blender -> OpenGL/Engine) ---
|
|
||||||
// Сохраняем вашу логику смены осей: X->Z, Y->X, Z->Y
|
|
||||||
|
|
||||||
for (size_t i = 0; i < result.PositionData.size(); i++)
|
|
||||||
{
|
|
||||||
Vector3f originalPos = result.PositionData[i];
|
|
||||||
result.PositionData[i].v[0] = originalPos.v[1]; // New X = Old Y
|
|
||||||
result.PositionData[i].v[1] = originalPos.v[2]; // New Y = Old Z
|
|
||||||
result.PositionData[i].v[2] = originalPos.v[0]; // New Z = Old X
|
|
||||||
|
|
||||||
Vector3f originalNorm = result.NormalData[i];
|
|
||||||
result.NormalData[i].v[0] = originalNorm.v[1];
|
|
||||||
result.NormalData[i].v[1] = originalNorm.v[2];
|
|
||||||
result.NormalData[i].v[2] = originalNorm.v[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Model loaded: " << numberVertices << " verts, " << numberTriangles << " tris." << std::endl;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Math.h"
|
|
||||||
#include "Renderer.h"
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
|
|
||||||
namespace ZL
|
|
||||||
{
|
|
||||||
VertexDataStruct LoadFromTextFile(const std::string& fileName, const std::string& ZIPFileName = "");
|
|
||||||
VertexDataStruct LoadFromTextFile02(const std::string& fileName, const std::string& ZIPFileName = "");
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#include "Utils.h"
|
|
||||||
#include <cstring>
|
|
||||||
#include <iterator>
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
//#include <zip.h>
|
|
||||||
|
|
||||||
namespace ZL
|
|
||||||
{
|
|
||||||
|
|
||||||
std::string readTextFile(const std::string& filename)
|
|
||||||
{
|
|
||||||
std::ifstream f(filename);
|
|
||||||
|
|
||||||
std::string str((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> readFile(const std::string& filename)
|
|
||||||
{
|
|
||||||
std::ifstream file(filename, std::ios::binary);
|
|
||||||
|
|
||||||
file.unsetf(std::ios::skipws);
|
|
||||||
|
|
||||||
std::streampos fileSize;
|
|
||||||
|
|
||||||
file.seekg(0, std::ios::end);
|
|
||||||
fileSize = file.tellg();
|
|
||||||
file.seekg(0, std::ios::beg);
|
|
||||||
|
|
||||||
std::vector<char> vec;
|
|
||||||
vec.reserve(fileSize);
|
|
||||||
|
|
||||||
vec.insert(vec.begin(),
|
|
||||||
std::istream_iterator<char>(file),
|
|
||||||
std::istream_iterator<char>());
|
|
||||||
|
|
||||||
return vec;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> readFileFromZIP(const std::string& filename, const std::string& zipfilename) {
|
|
||||||
/*const std::string zipPath = zipfilename;
|
|
||||||
int zipErr;
|
|
||||||
zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &zipErr);
|
|
||||||
if (!archive) {
|
|
||||||
throw std::runtime_error("Ошибка открытия ZIP: " + zipPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string cleanFilename = filename;
|
|
||||||
if (cleanFilename.rfind("./", 0) == 0) {
|
|
||||||
cleanFilename = cleanFilename.substr(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Ищем в ZIP: " << cleanFilename << std::endl;
|
|
||||||
|
|
||||||
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
|
||||||
if (!zipFile) {
|
|
||||||
zip_close(archive);
|
|
||||||
throw std::runtime_error("Файл не найден в ZIP: " + cleanFilename);
|
|
||||||
}
|
|
||||||
|
|
||||||
zip_stat_t fileStat;
|
|
||||||
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0) {
|
|
||||||
zip_fclose(zipFile);
|
|
||||||
zip_close(archive);
|
|
||||||
throw std::runtime_error("Ошибка чтения ZIP-статистики.");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> fileData;
|
|
||||||
fileData.resize(fileStat.size);
|
|
||||||
|
|
||||||
zip_fread(zipFile, fileData.data(), fileData.size());
|
|
||||||
|
|
||||||
zip_fclose(zipFile);
|
|
||||||
zip_close(archive);
|
|
||||||
|
|
||||||
return fileData;*/
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool findString(const char* in, char* list)
|
|
||||||
{
|
|
||||||
size_t thisLength = strlen(in);
|
|
||||||
while (*list != 0)
|
|
||||||
{
|
|
||||||
size_t length = strcspn(list, " ");
|
|
||||||
|
|
||||||
if (thisLength == length)
|
|
||||||
if (!strncmp(in, list, length))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
list += length;
|
|
||||||
list += 1;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY');
|
||||||
|
def buildAsApplication = !buildAsLibrary
|
||||||
|
if (buildAsApplication) {
|
||||||
|
apply plugin: 'com.android.application'
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
if (buildAsApplication) {
|
||||||
|
namespace "org.libsdl.app"
|
||||||
|
}
|
||||||
|
compileSdkVersion 34
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 19
|
||||||
|
targetSdkVersion 34
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
externalNativeBuild {
|
||||||
|
/*ndkBuild {
|
||||||
|
arguments "APP_PLATFORM=android-19"
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
|
}*/
|
||||||
|
cmake {
|
||||||
|
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
|
||||||
|
cppFlags "-std=c++11 -frtti -fexceptions"
|
||||||
|
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applicationVariants.all { variant ->
|
||||||
|
tasks["merge${variant.name.capitalize()}Assets"]
|
||||||
|
.dependsOn("externalNativeBuild${variant.name.capitalize()}")
|
||||||
|
}
|
||||||
|
if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) {
|
||||||
|
sourceSets.main {
|
||||||
|
jniLibs.srcDir 'libs'
|
||||||
|
}
|
||||||
|
externalNativeBuild {
|
||||||
|
/*ndkBuild {
|
||||||
|
path 'jni/Android.mk'
|
||||||
|
}*/
|
||||||
|
cmake {
|
||||||
|
path 'jni/CMakeLists.txt'
|
||||||
|
version '3.22.1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
lint {
|
||||||
|
abortOnError false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buildAsLibrary) {
|
||||||
|
libraryVariants.all { variant ->
|
||||||
|
variant.outputs.each { output ->
|
||||||
|
def outputFile = output.outputFile
|
||||||
|
if (outputFile != null && outputFile.name.endsWith(".aar")) {
|
||||||
|
def fileName = "org.libsdl.app.aar";
|
||||||
|
output.outputFile = new File(outputFile.parent, fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.6)
|
||||||
|
|
||||||
|
project(GAME)
|
||||||
|
|
||||||
|
if(POLICY CMP0079)
|
||||||
|
cmake_policy(SET CMP0079 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Копируем pnglibconf.h.prebuilt
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libpng/scripts/pnglibconf.h.prebuilt
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libpng/pnglibconf.h
|
||||||
|
)
|
||||||
|
|
||||||
|
# Сначала zlib
|
||||||
|
add_subdirectory(zlib)
|
||||||
|
|
||||||
|
# ВАЖНО: Установите опции ДО add_subdirectory(libpng)
|
||||||
|
# Libpng создает ДВЕ цели: png_shared (shared) и png_static (static)
|
||||||
|
# Мы хотим статическую библиотеку для Android
|
||||||
|
set(PNG_STATIC ON CACHE BOOL "Build static library" FORCE)
|
||||||
|
set(PNG_SHARED OFF CACHE BOOL "Don't build shared library" FORCE)
|
||||||
|
set(PNG_TESTS OFF CACHE BOOL "Disable tests" FORCE)
|
||||||
|
set(PNG_TOOLS OFF CACHE BOOL "Disable tools" FORCE)
|
||||||
|
set(PNG_EXECUTABLES OFF CACHE BOOL "Disable executables" FORCE)
|
||||||
|
set(PNG_DEBUG OFF CACHE BOOL "Disable debug" FORCE)
|
||||||
|
set(SKIP_INSTALL_ALL ON CACHE BOOL "Skip installation" FORCE)
|
||||||
|
|
||||||
|
# Для Android отключаем оптимизации
|
||||||
|
set(PNG_HARDWARE_OPTIMIZATIONS OFF CACHE BOOL "Disable hardware optimizations" FORCE)
|
||||||
|
set(PNG_ARM_NEON "off" CACHE STRING "Disable ARM NEON" FORCE)
|
||||||
|
|
||||||
|
# Добавляем libpng
|
||||||
|
add_subdirectory(libpng)
|
||||||
|
|
||||||
|
# Затем SDL
|
||||||
|
add_subdirectory(SDL)
|
||||||
|
|
||||||
|
# И ваш код
|
||||||
|
add_subdirectory(src)
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,62 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.6)
|
||||||
|
|
||||||
|
project(MY_APP CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
add_library(main SHARED
|
||||||
|
SDL_android_main.c
|
||||||
|
main.cpp
|
||||||
|
|
||||||
|
Utils.h
|
||||||
|
Utils.cpp
|
||||||
|
TextureManager.h
|
||||||
|
TextureManager.cpp
|
||||||
|
TextModel.h
|
||||||
|
TextModel.cpp
|
||||||
|
ShaderManager.h
|
||||||
|
ShaderManager.cpp
|
||||||
|
Renderer.h
|
||||||
|
Renderer.cpp
|
||||||
|
Math.h
|
||||||
|
Math.cpp
|
||||||
|
Environment.h
|
||||||
|
Environment.cpp
|
||||||
|
BoneAnimatedModel.h
|
||||||
|
BoneAnimatedModel.cpp
|
||||||
|
AnimatedModel.h
|
||||||
|
Game.h
|
||||||
|
Game.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Подключаем заголовки
|
||||||
|
target_include_directories(main PRIVATE
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../SDL/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../zlib
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../libpng
|
||||||
|
)
|
||||||
|
|
||||||
|
# ВАЖНО: Линкуемся с png_static (статика) или png_shared (динамика)
|
||||||
|
# Так как мы установили PNG_STATIC=ON и PNG_SHARED=OFF,
|
||||||
|
# должна создаться цель png_static
|
||||||
|
target_link_libraries(main
|
||||||
|
png_static # ← ЭТО ПРАВИЛЬНОЕ ИМЯ ЦЕЛИ!
|
||||||
|
z
|
||||||
|
SDL2
|
||||||
|
)
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
# OpenGL ES 2.0 или 3.0 для Android
|
||||||
|
find_library(OPENGLES2_LIB GLESv2)
|
||||||
|
find_library(OPENGLES1_LIB GLESv1_CM)
|
||||||
|
|
||||||
|
target_link_libraries(main
|
||||||
|
${OPENGLES2_LIB} # OpenGL ES 2.0/3.0
|
||||||
|
${OPENGLES1_LIB} # OpenGL ES 1.x (если нужно)
|
||||||
|
log
|
||||||
|
android
|
||||||
|
OpenSLES
|
||||||
|
dl
|
||||||
|
)
|
||||||
|
endif()
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
#include "Environment.h"
|
#include "Environment.h"
|
||||||
|
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include <GL/gl.h>
|
//#include <GL/gl.h>
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
int Environment::windowHeaderHeight = 0;
|
int Environment::windowHeaderHeight = 0;
|
||||||
@@ -3,8 +3,9 @@
|
|||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#endif
|
#endif
|
||||||
#include "OpenGlExtensions.h"
|
//#include "OpenGlExtensions.h"
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
class Environment {
|
class Environment {
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
#include "Game.h"
|
||||||
|
#include "AnimatedModel.h"
|
||||||
|
#include "BoneAnimatedModel.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include "TextureManager.h"
|
||||||
|
#include "TextModel.h"
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
const char *CONST_ZIP_FILE = "";
|
||||||
|
|
||||||
|
Game::Game()
|
||||||
|
: window(nullptr), glContext(nullptr), newTickCount(0), lastTickCount(0),
|
||||||
|
resourcesLoaded(false), modelLoaded(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::~Game() {
|
||||||
|
if (glContext) {
|
||||||
|
SDL_GL_DeleteContext(glContext);
|
||||||
|
}
|
||||||
|
if (window) {
|
||||||
|
SDL_DestroyWindow(window);
|
||||||
|
}
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::setup() {
|
||||||
|
glContext = SDL_GL_CreateContext(ZL::Environment::window);
|
||||||
|
|
||||||
|
// Initialize renderer
|
||||||
|
renderer.InitOpenGL();
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Game", "Start for Android");
|
||||||
|
|
||||||
|
const char* testFiles[] = {
|
||||||
|
"resources/spaceship004.txt",
|
||||||
|
"shaders/default.vertex",
|
||||||
|
"shaders/default_android.fragment",
|
||||||
|
"shaders/defaultColor.vertex",
|
||||||
|
"shaders/defaultColor_android.fragment",
|
||||||
|
"resources/sship001x.bmp",
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; testFiles[i] != nullptr; i++) {
|
||||||
|
SDL_RWops* file = SDL_RWFromFile(testFiles[i], "rb");
|
||||||
|
if (file) {
|
||||||
|
Sint64 size = SDL_RWsize(file);
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Game", "Found: %s (size: %lld)", testFiles[i], size);
|
||||||
|
SDL_RWclose(file);
|
||||||
|
} else {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "Game", "Not found: %s (SDL error: %s)",
|
||||||
|
testFiles[i], SDL_GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Game", "Shaders...");
|
||||||
|
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("default",
|
||||||
|
"shaders/default.vertex",
|
||||||
|
"shaders/default_android.fragment",
|
||||||
|
CONST_ZIP_FILE);
|
||||||
|
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("defaultColor",
|
||||||
|
"shaders/defaultColor.vertex",
|
||||||
|
"shaders/defaultColor_android.fragment",
|
||||||
|
CONST_ZIP_FILE);
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Game", "Textures...");
|
||||||
|
try {
|
||||||
|
spaceshipTexture = std::make_shared<Texture>(
|
||||||
|
CreateTextureDataFromBmp32("resources/sship001x.bmp")
|
||||||
|
);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
spaceshipTexture = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Game", "Model...");
|
||||||
|
|
||||||
|
std::string modelPaths[] = {
|
||||||
|
"resources/spaceship004.txt",
|
||||||
|
""
|
||||||
|
};
|
||||||
|
|
||||||
|
bool modelLoadSuccess = false;
|
||||||
|
for (int i = 0; !modelLoadSuccess && !modelPaths[i].empty(); i++) {
|
||||||
|
try {
|
||||||
|
spaceshipBase = LoadFromTextFile02(modelPaths[i]);
|
||||||
|
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
|
||||||
|
spaceship.AssignFrom(spaceshipBase);
|
||||||
|
spaceship.RefreshVBO();
|
||||||
|
modelLoaded = true;
|
||||||
|
modelLoadSuccess = true;
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Game", "Model loaded successfully from: %s", modelPaths[i].c_str());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "Game", "Failed to load model from %s: %s",
|
||||||
|
modelPaths[i].c_str(), e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resourcesLoaded = (spaceshipTexture != nullptr && modelLoaded);
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Game", "Setup failed: %s", e.what());
|
||||||
|
resourcesLoaded = false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Десктопная версия
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("default",
|
||||||
|
"./shaders/default.vertex",
|
||||||
|
"./shaders/default_desktop.fragment",
|
||||||
|
CONST_ZIP_FILE);
|
||||||
|
renderer.shaderManager.AddShaderFromFiles("defaultColor",
|
||||||
|
"./shaders/defaultColor.vertex",
|
||||||
|
"./shaders/defaultColor_desktop.fragment",
|
||||||
|
CONST_ZIP_FILE);
|
||||||
|
|
||||||
|
//Load texture
|
||||||
|
spaceshipTexture = std::make_shared<Texture>(
|
||||||
|
CreateTextureDataFromPng("./resources/sship001x.png"));
|
||||||
|
spaceshipBase = LoadFromTextFile02("./resources/spaceship004.txt");
|
||||||
|
spaceshipBase.RotateByMatrix(QuatToMatrix(QuatFromRotateAroundY(M_PI / 2.0)));
|
||||||
|
spaceship.AssignFrom(spaceshipBase);
|
||||||
|
spaceship.RefreshVBO();
|
||||||
|
resourcesLoaded = true;
|
||||||
|
modelLoaded = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::drawScene() {
|
||||||
|
static const std::string defaultShaderName = "default";
|
||||||
|
static const std::string vPositionName = "vPosition";
|
||||||
|
static const std::string vTexCoordName = "vTexCoord";
|
||||||
|
static const std::string textureUniformName = "Texture";
|
||||||
|
|
||||||
|
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
|
||||||
|
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
glViewport(0, 0, Environment::width, Environment::height);
|
||||||
|
|
||||||
|
if (!resourcesLoaded) {
|
||||||
|
glClearColor(0.2f, 0.3f, 0.8f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.shaderManager.PushShader(defaultShaderName);
|
||||||
|
renderer.RenderUniform1i(textureUniformName, 0);
|
||||||
|
renderer.EnableVertexAttribArray(vPositionName);
|
||||||
|
renderer.EnableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
|
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
|
||||||
|
static_cast<float>(Environment::width) /
|
||||||
|
static_cast<float>(Environment::height),
|
||||||
|
1, 1000);
|
||||||
|
renderer.PushMatrix();
|
||||||
|
|
||||||
|
renderer.LoadIdentity();
|
||||||
|
renderer.TranslateMatrix({0, 0, -1.0f * Environment::zoom});
|
||||||
|
renderer.RotateMatrix(QuatFromRotateAroundX(M_PI / 6.0));
|
||||||
|
//renderer.RotateMatrix(QuatFromRotateAroundX(Environment::cameraAlpha));
|
||||||
|
|
||||||
|
if (spaceshipTexture) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
|
||||||
|
renderer.DrawVertexRenderStruct(spaceship);
|
||||||
|
} else {
|
||||||
|
renderer.DrawVertexRenderStruct(spaceship);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.PopMatrix();
|
||||||
|
renderer.PopProjectionMatrix();
|
||||||
|
renderer.DisableVertexAttribArray(vPositionName);
|
||||||
|
renderer.DisableVertexAttribArray(vTexCoordName);
|
||||||
|
|
||||||
|
renderer.shaderManager.PopShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::processTickCount() {
|
||||||
|
|
||||||
|
if (lastTickCount == 0) {
|
||||||
|
lastTickCount = SDL_GetTicks64();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
newTickCount = SDL_GetTicks64();
|
||||||
|
if (newTickCount - lastTickCount > CONST_TIMER_INTERVAL) {
|
||||||
|
size_t delta = (newTickCount - lastTickCount > CONST_MAX_TIME_INTERVAL) ?
|
||||||
|
CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount;
|
||||||
|
|
||||||
|
//gameObjects.updateScene(delta);
|
||||||
|
|
||||||
|
Environment::cameraAlpha = Environment::cameraAlpha + delta * M_PI / 10000.f;
|
||||||
|
|
||||||
|
lastTickCount = newTickCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::render() {
|
||||||
|
SDL_GL_MakeCurrent(ZL::Environment::window, glContext);
|
||||||
|
|
||||||
|
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
drawScene();
|
||||||
|
processTickCount();
|
||||||
|
|
||||||
|
SDL_GL_SwapWindow(ZL::Environment::window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Game::update() {
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (event.type == SDL_QUIT) {
|
||||||
|
Environment::exitGameLoop = true;
|
||||||
|
|
||||||
|
} else if (event.type == SDL_MOUSEWHEEL) {
|
||||||
|
static const float zoomstep = 2.0f;
|
||||||
|
if (event.wheel.y > 0) {
|
||||||
|
Environment::zoom -= zoomstep;
|
||||||
|
} else if (event.wheel.y < 0) {
|
||||||
|
Environment::zoom += zoomstep;
|
||||||
|
}
|
||||||
|
if (Environment::zoom < zoomstep) {
|
||||||
|
Environment::zoom = zoomstep;
|
||||||
|
}
|
||||||
|
/*if (Environment::zoom > 4) {
|
||||||
|
Environment::zoom = 4;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//this->modelMeshRender.data.Scale(0.5);
|
||||||
|
//this->modelMeshRender.RefreshVBO();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ZL
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Renderer.h"
|
||||||
|
#include "Environment.h"
|
||||||
|
#include "TextureManager.h"
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
class Game {
|
||||||
|
public:
|
||||||
|
Game();
|
||||||
|
|
||||||
|
~Game();
|
||||||
|
|
||||||
|
void setup();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
void render();
|
||||||
|
|
||||||
|
bool shouldExit() const { return Environment::exitGameLoop; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void processTickCount();
|
||||||
|
|
||||||
|
void drawScene();
|
||||||
|
|
||||||
|
SDL_Window *window;
|
||||||
|
SDL_GLContext glContext;
|
||||||
|
Renderer renderer;
|
||||||
|
|
||||||
|
size_t newTickCount;
|
||||||
|
size_t lastTickCount;
|
||||||
|
|
||||||
|
bool resourcesLoaded;
|
||||||
|
bool modelLoaded;
|
||||||
|
|
||||||
|
static const size_t CONST_TIMER_INTERVAL = 10;
|
||||||
|
static const size_t CONST_MAX_TIME_INTERVAL = 1000;
|
||||||
|
|
||||||
|
std::shared_ptr <Texture> spaceshipTexture;
|
||||||
|
VertexDataStruct spaceshipBase;
|
||||||
|
VertexRenderStruct spaceship;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ZL
|
||||||
Executable → Regular
File renamed without changes.
Executable → Regular
File renamed without changes.
Executable → Regular
+10
-7
@@ -21,19 +21,19 @@ namespace ZL {
|
|||||||
VAOHolder::VAOHolder()
|
VAOHolder::VAOHolder()
|
||||||
{
|
{
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
|
#if defined(_WIN32) || defined(__linux__) && !defined(__ANDROID__)
|
||||||
glGenVertexArrays(1, &vao);
|
glGenVertexArrays(1, &vao);
|
||||||
|
#else
|
||||||
|
vao = 0;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
VAOHolder::~VAOHolder()
|
VAOHolder::~VAOHolder()
|
||||||
{
|
{
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
|
#if defined(_WIN32) || defined(__linux__) && !defined(__ANDROID__)
|
||||||
#ifdef __linux__
|
|
||||||
glDeleteVertexArrays(1, &vao);
|
glDeleteVertexArrays(1, &vao);
|
||||||
#else
|
|
||||||
//Windows
|
|
||||||
glDeleteVertexArray(1, &vao);
|
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -201,12 +201,13 @@ namespace ZL {
|
|||||||
//Check if main thread, check if data is not empty...
|
//Check if main thread, check if data is not empty...
|
||||||
|
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
|
#if defined(_WIN32) || defined(__linux__) && !defined(__ANDROID__)
|
||||||
if (!vao)
|
if (!vao)
|
||||||
{
|
{
|
||||||
vao = std::make_shared<VAOHolder>();
|
vao = std::make_shared<VAOHolder>();
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindVertexArray(vao->getBuffer());
|
glBindVertexArray(vao->getBuffer());
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
if (!positionVBO)
|
if (!positionVBO)
|
||||||
{
|
{
|
||||||
@@ -346,12 +347,14 @@ namespace ZL {
|
|||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
#ifndef EMSCRIPTEN
|
#ifndef EMSCRIPTEN
|
||||||
|
#if defined(_WIN32) || defined(__linux__) && !defined(__ANDROID__)
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
glDepthFunc(GL_LEQUAL);
|
glDepthFunc(GL_LEQUAL);
|
||||||
|
|
||||||
CheckGlError();
|
//CheckGlError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::PushProjectionMatrix(float width, float height, float zNear, float zFar)
|
void Renderer::PushProjectionMatrix(float width, float height, float zNear, float zFar)
|
||||||
Executable → Regular
+3
-1
@@ -1,10 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "OpenGlExtensions.h"
|
//#include "OpenGlExtensions.h"
|
||||||
#include "Math.h"
|
#include "Math.h"
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include "ShaderManager.h"
|
#include "ShaderManager.h"
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#include <jni.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
extern int SDL_main(int argc, char *argv[]);
|
||||||
|
|
||||||
|
void Java_org_libsdl_app_SDLActivity_nativeInit(JNIEnv* env, jclass cls, jobject obj) {
|
||||||
|
/* Run the application code! */
|
||||||
|
SDL_main(0, NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
#include "ShaderManager.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
ShaderResource::ShaderResource(const std::string &vertexCode, const std::string &fragmentCode) {
|
||||||
|
|
||||||
|
const int CONST_INFOLOG_LENGTH = 256;
|
||||||
|
|
||||||
|
char infoLog[CONST_INFOLOG_LENGTH];
|
||||||
|
int infoLogLength;
|
||||||
|
|
||||||
|
int vertexShaderCompiled;
|
||||||
|
int fragmentShaderCompiled;
|
||||||
|
int programLinked;
|
||||||
|
|
||||||
|
GLuint vertexShader;
|
||||||
|
GLuint fragmentShader;
|
||||||
|
|
||||||
|
int vertexCodeLength = static_cast<int>(strlen(vertexCode.c_str()));
|
||||||
|
int fragmentCodeLength = static_cast<int>(strlen(fragmentCode.c_str()));
|
||||||
|
|
||||||
|
const char *vc = &vertexCode[0];
|
||||||
|
const char *fc = &fragmentCode[0];
|
||||||
|
|
||||||
|
vertexShader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vertexShader, 1, &(vc), &vertexCodeLength);
|
||||||
|
|
||||||
|
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(fragmentShader, 1, &(fc), &fragmentCodeLength);
|
||||||
|
|
||||||
|
glCompileShader(vertexShader);
|
||||||
|
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &vertexShaderCompiled);
|
||||||
|
glGetShaderInfoLog(vertexShader, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
||||||
|
|
||||||
|
glCompileShader(fragmentShader);
|
||||||
|
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &fragmentShaderCompiled);
|
||||||
|
glGetShaderInfoLog(fragmentShader, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
||||||
|
|
||||||
|
if (!vertexShaderCompiled) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "ShaderManager",
|
||||||
|
"Failed to compile vertex shader: %s", infoLog);
|
||||||
|
#endif
|
||||||
|
throw std::runtime_error("Failed to compile vertex shader code!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fragmentShaderCompiled) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "ShaderManager",
|
||||||
|
"Failed to compile fragment shader: %s", infoLog);
|
||||||
|
#endif
|
||||||
|
throw std::runtime_error("Failed to compile fragment shader code!");
|
||||||
|
}
|
||||||
|
|
||||||
|
shaderProgram = glCreateProgram();
|
||||||
|
|
||||||
|
glAttachShader(shaderProgram, vertexShader);
|
||||||
|
glAttachShader(shaderProgram, fragmentShader);
|
||||||
|
|
||||||
|
glLinkProgram(shaderProgram);
|
||||||
|
|
||||||
|
glDeleteShader(vertexShader);
|
||||||
|
glDeleteShader(fragmentShader);
|
||||||
|
|
||||||
|
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &programLinked);
|
||||||
|
glGetProgramInfoLog(shaderProgram, CONST_INFOLOG_LENGTH, &infoLogLength, infoLog);
|
||||||
|
|
||||||
|
if (!programLinked) {
|
||||||
|
shaderProgram = 0;
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "ShaderManager",
|
||||||
|
"Failed to link shader program: %s", infoLog);
|
||||||
|
#endif
|
||||||
|
throw std::runtime_error("Failed to link shader program!");
|
||||||
|
}
|
||||||
|
|
||||||
|
//================= Parsing all uniforms ================
|
||||||
|
|
||||||
|
int dummySize; //Dummy
|
||||||
|
int dummyLen; //Dummy
|
||||||
|
GLenum dummyEnum;
|
||||||
|
|
||||||
|
int activeUniforms;
|
||||||
|
|
||||||
|
const int CONST_UNIFORM_NAME_LENGTH = 256;
|
||||||
|
char uniformName[CONST_UNIFORM_NAME_LENGTH];
|
||||||
|
|
||||||
|
glGetProgramiv(shaderProgram, GL_ACTIVE_UNIFORMS, &activeUniforms);
|
||||||
|
|
||||||
|
for (int i = 0; i < activeUniforms; i++) {
|
||||||
|
glGetActiveUniform(shaderProgram, i, CONST_UNIFORM_NAME_LENGTH, &dummyLen, &dummySize,
|
||||||
|
&dummyEnum, uniformName);
|
||||||
|
|
||||||
|
uniformList[uniformName] = glGetUniformLocation(shaderProgram, uniformName);
|
||||||
|
}
|
||||||
|
|
||||||
|
//================= Parsing all attributes ================
|
||||||
|
int activeAttribs;
|
||||||
|
|
||||||
|
const int CONST_ATTRIB_NAME_LENGTH = 256;
|
||||||
|
char attribName[CONST_ATTRIB_NAME_LENGTH];
|
||||||
|
|
||||||
|
glGetProgramiv(shaderProgram, GL_ACTIVE_ATTRIBUTES, &activeAttribs);
|
||||||
|
|
||||||
|
for (int i = 0; i < activeAttribs; i++) {
|
||||||
|
glGetActiveAttrib(shaderProgram, i, CONST_ATTRIB_NAME_LENGTH, &dummyLen, &dummySize,
|
||||||
|
&dummyEnum, attribName);
|
||||||
|
attribList[attribName] = glGetAttribLocation(shaderProgram, attribName);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "ShaderManager",
|
||||||
|
"Shader created successfully, program ID: %u", shaderProgram);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderResource::~ShaderResource() {
|
||||||
|
if (shaderProgram != 0) {
|
||||||
|
glDeleteProgram(shaderProgram);
|
||||||
|
|
||||||
|
shaderProgram = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint ShaderResource::getShaderProgram() {
|
||||||
|
return shaderProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ShaderManager::AddShaderFromFiles(const std::string &shaderName,
|
||||||
|
const std::string &vertexShaderFileName,
|
||||||
|
const std::string &fragmentShaderFileName,
|
||||||
|
const std::string &ZIPFileName) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "ShaderManager",
|
||||||
|
"Loading shader: %s", shaderName.c_str());
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "ShaderManager",
|
||||||
|
"Vertex shader: %s", vertexShaderFileName.c_str());
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "ShaderManager",
|
||||||
|
"Fragment shader: %s", fragmentShaderFileName.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string vertexShader;
|
||||||
|
std::string fragmentShader;
|
||||||
|
|
||||||
|
if (!ZIPFileName.empty()) {
|
||||||
|
|
||||||
|
std::vector<char> vertexShaderData;
|
||||||
|
std::vector<char> fragmentShaderData;
|
||||||
|
|
||||||
|
vertexShaderData = readFileFromZIP(vertexShaderFileName, ZIPFileName);
|
||||||
|
fragmentShaderData = readFileFromZIP(fragmentShaderFileName, ZIPFileName);
|
||||||
|
|
||||||
|
vertexShader = std::string(vertexShaderData.begin(), vertexShaderData.end());
|
||||||
|
fragmentShader = std::string(fragmentShaderData.begin(), fragmentShaderData.end());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
vertexShader = readTextFile(vertexShaderFileName);
|
||||||
|
fragmentShader = readTextFile(fragmentShaderFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
///std::cout << "Shader: "<< vertexShader << std::endl;
|
||||||
|
shaderResourceMap[shaderName] = std::make_shared<ShaderResource>(vertexShader,
|
||||||
|
fragmentShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderManager::PushShader(const std::string &shaderName) {
|
||||||
|
if (shaderStack.size() >= CONST_MAX_SHADER_STACK_SIZE) {
|
||||||
|
throw std::runtime_error("Shader stack overflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shaderResourceMap.find(shaderName) == shaderResourceMap.end()) {
|
||||||
|
throw std::runtime_error("Shader does not exist!");
|
||||||
|
}
|
||||||
|
|
||||||
|
shaderStack.push(shaderName);
|
||||||
|
|
||||||
|
glUseProgram(shaderResourceMap[shaderName]->getShaderProgram());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ShaderManager::PopShader() {
|
||||||
|
if (shaderStack.size() == 0) {
|
||||||
|
throw std::runtime_error("Shader stack underflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
shaderStack.pop();
|
||||||
|
|
||||||
|
if (shaderStack.size() == 0) {
|
||||||
|
glUseProgram(0);
|
||||||
|
} else {
|
||||||
|
glUseProgram(shaderResourceMap[shaderStack.top()]->getShaderProgram());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr <ShaderResource> ShaderManager::GetCurrentShader() {
|
||||||
|
if (shaderStack.size() == 0) {
|
||||||
|
throw std::runtime_error("Shader stack underflow!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return shaderResourceMap[shaderStack.top()];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ShaderSetter::ShaderSetter(ShaderManager &inShaderManager, const std::string &shaderName)
|
||||||
|
: shaderManager(inShaderManager) {
|
||||||
|
shaderManager.PushShader(shaderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderSetter::~ShaderSetter() {
|
||||||
|
shaderManager.PopShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Executable → Regular
+3
-1
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "OpenGlExtensions.h"
|
//#include "OpenGlExtensions.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
#include "TextModel.h"
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
namespace ZL {
|
||||||
|
|
||||||
|
|
||||||
|
VertexDataStruct LoadFromTextFile(const std::string &fileName, const std::string &ZIPFileName) {
|
||||||
|
VertexDataStruct result;
|
||||||
|
std::ifstream filestream;
|
||||||
|
std::istringstream zipStream;
|
||||||
|
|
||||||
|
if (!ZIPFileName.empty()) {
|
||||||
|
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||||
|
std::string fileContents(fileData.begin(), fileData.end());
|
||||||
|
zipStream.str(fileContents);
|
||||||
|
} else {
|
||||||
|
filestream.open(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем ссылку f на нужный поток – после этого код ниже остается без изменений
|
||||||
|
std::istream &f = (!ZIPFileName.empty()) ? static_cast<std::istream &>(zipStream)
|
||||||
|
: static_cast<std::istream &>(filestream);
|
||||||
|
|
||||||
|
|
||||||
|
//Skip first 5 lines
|
||||||
|
std::string tempLine;
|
||||||
|
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
|
||||||
|
static const std::regex pattern_count(R"(\d+)");
|
||||||
|
static const std::regex pattern_float(R"([-]?\d+\.\d+)");
|
||||||
|
static const std::regex pattern_int(R"([-]?\d+)");
|
||||||
|
|
||||||
|
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
int numberVertices;
|
||||||
|
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
std::string number_str = match.str();
|
||||||
|
numberVertices = std::stoi(number_str);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("No number found in the input string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector <Vector3f> vertices;
|
||||||
|
|
||||||
|
vertices.resize(numberVertices);
|
||||||
|
for (int i = 0; i < numberVertices; i++) {
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertices[i] = Vector3f{floatValues[0], floatValues[1], floatValues[2]};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "UV Coordinates" << std::endl;
|
||||||
|
|
||||||
|
std::getline(f, tempLine); //===UV Coordinates:
|
||||||
|
|
||||||
|
std::getline(f, tempLine); //triangle count
|
||||||
|
int numberTriangles;
|
||||||
|
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
std::string number_str = match.str();
|
||||||
|
numberTriangles = std::stoi(number_str);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("No number found in the input string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Now process UVs
|
||||||
|
std::vector <std::array<Vector2f, 3>> uvCoords;
|
||||||
|
|
||||||
|
uvCoords.resize(numberTriangles);
|
||||||
|
|
||||||
|
for (int i = 0; i < numberTriangles; i++) {
|
||||||
|
std::getline(f, tempLine); //Face 0
|
||||||
|
|
||||||
|
int uvCount;
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
std::string number_str = match.str();
|
||||||
|
uvCount = std::stoi(number_str);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("No number found in the input string.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uvCount != 3) {
|
||||||
|
throw std::runtime_error("more than 3 uvs");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
std::getline(f, tempLine); //UV <Vector (-0.3661, -1.1665)>
|
||||||
|
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
floatValues.clear();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (floatValues.size() != 2) {
|
||||||
|
throw std::runtime_error("more than 2 uvs---");
|
||||||
|
}
|
||||||
|
|
||||||
|
uvCoords[i][j] = Vector2f{floatValues[0], floatValues[1]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Normals go" << std::endl;
|
||||||
|
|
||||||
|
std::getline(f, tempLine); //===Normals:
|
||||||
|
|
||||||
|
|
||||||
|
std::vector <Vector3f> normals;
|
||||||
|
|
||||||
|
normals.resize(numberVertices);
|
||||||
|
for (int i = 0; i < numberVertices; i++) {
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
normals[i] = Vector3f{floatValues[0], floatValues[1], floatValues[2]};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Triangles go:" << std::endl;
|
||||||
|
|
||||||
|
std::getline(f, tempLine); //===Triangles: 3974
|
||||||
|
|
||||||
|
|
||||||
|
std::vector <std::array<int, 3>> triangles;
|
||||||
|
|
||||||
|
triangles.resize(numberTriangles);
|
||||||
|
for (int i = 0; i < numberTriangles; i++) {
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
|
||||||
|
std::vector<int> intValues;
|
||||||
|
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_int)) {
|
||||||
|
intValues.push_back(std::stoi(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
triangles[i] = {intValues[0], intValues[1], intValues[2]};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "Process vertices" << std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Now let's process vertices
|
||||||
|
|
||||||
|
for (int i = 0; i < numberTriangles; i++) {
|
||||||
|
|
||||||
|
result.PositionData.push_back(vertices[triangles[i][0]]);
|
||||||
|
result.PositionData.push_back(vertices[triangles[i][1]]);
|
||||||
|
result.PositionData.push_back(vertices[triangles[i][2]]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
result.NormalData.push_back(normals[triangles[i][0]]);
|
||||||
|
result.NormalData.push_back(normals[triangles[i][1]]);
|
||||||
|
result.NormalData.push_back(normals[triangles[i][2]]);
|
||||||
|
*/
|
||||||
|
result.TexCoordData.push_back(uvCoords[i][0]);
|
||||||
|
result.TexCoordData.push_back(uvCoords[i][1]);
|
||||||
|
result.TexCoordData.push_back(uvCoords[i][2]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Swap from Blender format to OpenGL format
|
||||||
|
for (int i = 0; i < result.PositionData.size(); i++) {
|
||||||
|
Vector3f tempVec = result.PositionData[i];
|
||||||
|
result.PositionData[i].v[0] = tempVec.v[1];
|
||||||
|
result.PositionData[i].v[1] = tempVec.v[2];
|
||||||
|
result.PositionData[i].v[2] = tempVec.v[0];
|
||||||
|
|
||||||
|
/*
|
||||||
|
tempVec = result.NormalData[i];
|
||||||
|
result.NormalData[i].v[0] = tempVec.v[1];
|
||||||
|
result.NormalData[i].v[1] = tempVec.v[2];
|
||||||
|
result.NormalData[i].v[2] = tempVec.v[0];*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VertexDataStruct
|
||||||
|
LoadFromTextFile02(const std::string &fileName, const std::string &ZIPFileName) {
|
||||||
|
VertexDataStruct result;
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
// Для Android используем SDL_RWops для чтения файлов
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "TextModel",
|
||||||
|
"LoadFromTextFile02 called for Android: %s", fileName.c_str());
|
||||||
|
|
||||||
|
// Читаем файл с помощью readTextFile
|
||||||
|
std::string fileContent = readTextFile(fileName);
|
||||||
|
if (fileContent.empty()) {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "TextModel",
|
||||||
|
"Failed to read file: %s", fileName.c_str());
|
||||||
|
throw std::runtime_error("Failed to read file: " + fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "TextModel",
|
||||||
|
"File read successfully, size: %zu", fileContent.size());
|
||||||
|
|
||||||
|
std::istringstream f(fileContent);
|
||||||
|
#else
|
||||||
|
// Оригинальный код для десктопа
|
||||||
|
std::ifstream filestream;
|
||||||
|
std::istringstream zipStream;
|
||||||
|
|
||||||
|
if (!ZIPFileName.empty()) {
|
||||||
|
std::vector<char> fileData = readFileFromZIP(fileName, ZIPFileName);
|
||||||
|
std::string fileContents(fileData.begin(), fileData.end());
|
||||||
|
zipStream.str(fileContents);
|
||||||
|
f = static_cast<std::istream &>(zipStream);
|
||||||
|
} else {
|
||||||
|
filestream.open(fileName);
|
||||||
|
if (!filestream.is_open()) {
|
||||||
|
throw std::runtime_error("Failed to open file: " + fileName);
|
||||||
|
}
|
||||||
|
f = static_cast<std::istream &>(filestream);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string tempLine;
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
// Обновленные регулярки
|
||||||
|
static const std::regex pattern_count(R"(\d+)");
|
||||||
|
static const std::regex pattern_float(R"([-]?\d+(\.\d+)?)");
|
||||||
|
static const std::regex pattern_int(R"([-]?\d+)");
|
||||||
|
|
||||||
|
// --- 2. Парсинг Вершин (Pos + Norm + UV) ---
|
||||||
|
|
||||||
|
// Ищем заголовок ===Vertices
|
||||||
|
while (std::getline(f, tempLine)) {
|
||||||
|
if (tempLine.find("===Vertices") != std::string::npos) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numberVertices = 0;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberVertices = std::stoi(match.str());
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Vertices header not found or invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Временные буферы для хранения "уникальных" вершин перед разверткой по индексам
|
||||||
|
std::vector <Vector3f> tempPositions(numberVertices);
|
||||||
|
std::vector <Vector3f> tempNormals(numberVertices);
|
||||||
|
std::vector <Vector2f> tempUVs(numberVertices);
|
||||||
|
|
||||||
|
for (int i = 0; i < numberVertices; i++) {
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
// Строка вида: V 0: Pos(x, y, z) Norm(x, y, z) UV(u, v)
|
||||||
|
|
||||||
|
std::vector<float> floatValues;
|
||||||
|
floatValues.reserve(9); // ID + 3 pos + 3 norm + 2 uv
|
||||||
|
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_float)) {
|
||||||
|
floatValues.push_back(std::stof(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка, что у нас достаточно данных
|
||||||
|
if (floatValues.size() < 9) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "TextModel",
|
||||||
|
"Warning: vertex line has only %zu values, expected 9",
|
||||||
|
floatValues.size());
|
||||||
|
#endif
|
||||||
|
// Попробуем с меньшим количеством, возможно данные упрощенные
|
||||||
|
if (floatValues.size() >= 8) {
|
||||||
|
// Без ID
|
||||||
|
tempPositions[i] = Vector3f{floatValues[0], floatValues[1], floatValues[2]};
|
||||||
|
tempNormals[i] = Vector3f{floatValues[3], floatValues[4], floatValues[5]};
|
||||||
|
tempUVs[i] = Vector2f{floatValues[6], floatValues[7]};
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Malformed vertex line at index " + std::to_string(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ID - floatValues[0]
|
||||||
|
// Pos - [1], [2], [3]
|
||||||
|
// Norm - [4], [5], [6]
|
||||||
|
// UV - [7], [8]
|
||||||
|
tempPositions[i] = Vector3f{floatValues[1], floatValues[2], floatValues[3]};
|
||||||
|
tempNormals[i] = Vector3f{floatValues[4], floatValues[5], floatValues[6]};
|
||||||
|
tempUVs[i] = Vector2f{floatValues[7], floatValues[8]};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 3. Парсинг Треугольников (Индексов) ---
|
||||||
|
|
||||||
|
// Пропускаем пустые строки до заголовка треугольников
|
||||||
|
while (std::getline(f, tempLine)) {
|
||||||
|
if (tempLine.find("===Triangles") != std::string::npos) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numberTriangles = 0;
|
||||||
|
if (std::regex_search(tempLine, match, pattern_count)) {
|
||||||
|
numberTriangles = std::stoi(match.str());
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("Triangles header not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Резервируем память в result, чтобы избежать лишних аллокаций
|
||||||
|
result.PositionData.reserve(numberTriangles * 3);
|
||||||
|
result.NormalData.reserve(numberTriangles * 3);
|
||||||
|
result.TexCoordData.reserve(numberTriangles * 3);
|
||||||
|
|
||||||
|
for (int i = 0; i < numberTriangles; i++) {
|
||||||
|
std::getline(f, tempLine);
|
||||||
|
// Строка вида: Tri: 0 1 2
|
||||||
|
|
||||||
|
std::vector<int> indices;
|
||||||
|
indices.reserve(3);
|
||||||
|
|
||||||
|
auto b = tempLine.cbegin();
|
||||||
|
auto e = tempLine.cend();
|
||||||
|
while (std::regex_search(b, e, match, pattern_int)) {
|
||||||
|
indices.push_back(std::stoi(match.str()));
|
||||||
|
b = match.suffix().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indices.size() != 3) {
|
||||||
|
throw std::runtime_error("Malformed triangle line at index " + std::to_string(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 4. Заполнение VertexDataStruct (Flattening) ---
|
||||||
|
for (int k = 0; k < 3; k++) {
|
||||||
|
int idx = indices[k];
|
||||||
|
result.PositionData.push_back(tempPositions[idx]);
|
||||||
|
result.NormalData.push_back(tempNormals[idx]);
|
||||||
|
result.TexCoordData.push_back(tempUVs[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 5. Конвертация координат (Blender -> OpenGL/Engine) ---
|
||||||
|
for (size_t i = 0; i < result.PositionData.size(); i++) {
|
||||||
|
Vector3f originalPos = result.PositionData[i];
|
||||||
|
result.PositionData[i].v[0] = originalPos.v[1]; // New X = Old Y
|
||||||
|
result.PositionData[i].v[1] = originalPos.v[2]; // New Y = Old Z
|
||||||
|
result.PositionData[i].v[2] = originalPos.v[0]; // New Z = Old X
|
||||||
|
|
||||||
|
Vector3f originalNorm = result.NormalData[i];
|
||||||
|
result.NormalData[i].v[0] = originalNorm.v[1];
|
||||||
|
result.NormalData[i].v[1] = originalNorm.v[2];
|
||||||
|
result.NormalData[i].v[2] = originalNorm.v[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "TextModel",
|
||||||
|
"Model loaded successfully: %d verts, %d tris",
|
||||||
|
numberVertices, numberTriangles);
|
||||||
|
#else
|
||||||
|
std::cout << "Model loaded: " << numberVertices << " verts, " << numberTriangles << " tris."
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Math.h"
|
||||||
|
#include "Renderer.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
VertexDataStruct
|
||||||
|
LoadFromTextFile(const std::string &fileName, const std::string &ZIPFileName = "");
|
||||||
|
|
||||||
|
VertexDataStruct
|
||||||
|
LoadFromTextFile02(const std::string &fileName, const std::string &ZIPFileName = "");
|
||||||
|
}
|
||||||
Executable → Regular
+4
-4
@@ -21,17 +21,17 @@ namespace ZL
|
|||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, texID);
|
glBindTexture(GL_TEXTURE_2D, texID);
|
||||||
|
|
||||||
CheckGlError();
|
//CheckGlError();
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
CheckGlError();
|
//CheckGlError();
|
||||||
|
|
||||||
//This should be only for Windows
|
//This should be only for Windows
|
||||||
//glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
|
//glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
|
||||||
|
|
||||||
CheckGlError();
|
//CheckGlError();
|
||||||
|
|
||||||
if (texData.bitSize == TextureDataStruct::BS_24BIT)
|
if (texData.bitSize == TextureDataStruct::BS_24BIT)
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ namespace ZL
|
|||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(texData.width), static_cast<GLsizei>(texData.height), 0, GL_RGBA, GL_UNSIGNED_BYTE, &texData.data[0]);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, static_cast<GLsizei>(texData.width), static_cast<GLsizei>(texData.height), 0, GL_RGBA, GL_UNSIGNED_BYTE, &texData.data[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckGlError();
|
//CheckGlError();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Executable → Regular
+3
-1
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "OpenGlExtensions.h"
|
//#include "OpenGlExtensions.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
namespace ZL
|
namespace ZL
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
#include "Utils.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
//#include <zip.h>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ZL {
|
||||||
|
std::string readTextFile(const std::string &filename) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
SDL_RWops* file = SDL_RWFromFile(filename.c_str(), "rb");
|
||||||
|
if (!file) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Utils",
|
||||||
|
"Failed to open: %s", filename.c_str());
|
||||||
|
#endif
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Sint64 size = SDL_RWsize(file);
|
||||||
|
if (size <= 0) {
|
||||||
|
SDL_RWclose(file);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content(size, '\0');
|
||||||
|
if (SDL_RWread(file, &content[0], size, 1) != 1) {
|
||||||
|
SDL_RWclose(file);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RWclose(file);
|
||||||
|
return content;
|
||||||
|
#else
|
||||||
|
std::ifstream f(filename);
|
||||||
|
if (!f.is_open()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return std::string((std::istreambuf_iterator<char>(f)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> readFile(const std::string &filename) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
SDL_RWops* file = SDL_RWFromFile(filename.c_str(), "rb");
|
||||||
|
if (!file) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Sint64 size = SDL_RWsize(file);
|
||||||
|
if (size <= 0) {
|
||||||
|
SDL_RWclose(file);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> data(size);
|
||||||
|
if (SDL_RWread(file, data.data(), size, 1) != 1) {
|
||||||
|
SDL_RWclose(file);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RWclose(file);
|
||||||
|
return data;
|
||||||
|
#else
|
||||||
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
|
if (!file) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize size = file.tellg();
|
||||||
|
if (size <= 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
std::vector<char> buffer(size);
|
||||||
|
|
||||||
|
if (!file.read(buffer.data(), size)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> readFileFromZIP(const std::string &filename, const std::string &zipfilename) {
|
||||||
|
/*const std::string zipPath = zipfilename;
|
||||||
|
int zipErr;
|
||||||
|
zip_t* archive = zip_open(zipPath.c_str(), ZIP_RDONLY, &zipErr);
|
||||||
|
if (!archive) {
|
||||||
|
throw std::runtime_error("Ошибка открытия ZIP: " + zipPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cleanFilename = filename;
|
||||||
|
if (cleanFilename.rfind("./", 0) == 0) {
|
||||||
|
cleanFilename = cleanFilename.substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Ищем в ZIP: " << cleanFilename << std::endl;
|
||||||
|
|
||||||
|
zip_file_t* zipFile = zip_fopen(archive, cleanFilename.c_str(), 0);
|
||||||
|
if (!zipFile) {
|
||||||
|
zip_close(archive);
|
||||||
|
throw std::runtime_error("Файл не найден в ZIP: " + cleanFilename);
|
||||||
|
}
|
||||||
|
|
||||||
|
zip_stat_t fileStat;
|
||||||
|
if (zip_stat(archive, cleanFilename.c_str(), 0, &fileStat) != 0) {
|
||||||
|
zip_fclose(zipFile);
|
||||||
|
zip_close(archive);
|
||||||
|
throw std::runtime_error("Ошибка чтения ZIP-статистики.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char> fileData;
|
||||||
|
fileData.resize(fileStat.size);
|
||||||
|
|
||||||
|
zip_fread(zipFile, fileData.data(), fileData.size());
|
||||||
|
|
||||||
|
zip_fclose(zipFile);
|
||||||
|
zip_close(archive);
|
||||||
|
|
||||||
|
return fileData;*/
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool findString(const char *in, char *list) {
|
||||||
|
size_t thisLength = strlen(in);
|
||||||
|
while (*list != 0) {
|
||||||
|
size_t length = strcspn(list, " ");
|
||||||
|
if (thisLength == length && !strncmp(in, list, length))
|
||||||
|
return true;
|
||||||
|
list += length + 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
Executable → Regular
File renamed without changes.
@@ -0,0 +1,199 @@
|
|||||||
|
#include "Game.h"
|
||||||
|
#include "Environment.h"
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
#include <android/log.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ZL::Game game;
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
|
||||||
|
extern "C" int SDL_main(int argc, char* argv[]) {
|
||||||
|
#else
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
// Инициализация SDL перед получением информации о дисплее
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL init failed: %s", SDL_GetError());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_DisplayMode displayMode;
|
||||||
|
if (SDL_GetCurrentDisplayMode(0, &displayMode) != 0) {
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GetCurrentDisplayMode failed: %s", SDL_GetError());
|
||||||
|
SDL_Quit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
ZL::Environment::width = displayMode.w;
|
||||||
|
ZL::Environment::height = displayMode.h;
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Game", "Display resolution: %dx%d",
|
||||||
|
ZL::Environment::width, ZL::Environment::height);
|
||||||
|
#else
|
||||||
|
constexpr int CONST_WIDTH = 1280;
|
||||||
|
constexpr int CONST_HEIGHT = 720;
|
||||||
|
ZL::Environment::width = CONST_WIDTH;
|
||||||
|
ZL::Environment::height = CONST_HEIGHT;
|
||||||
|
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
||||||
|
SDL_Log("SDL init failed: %s", SDL_GetError());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
||||||
|
std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||||
|
|
||||||
|
SDL_Window* win = SDL_CreateWindow("Space Ship Game",
|
||||||
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
|
ZL::Environment::width, ZL::Environment::height,
|
||||||
|
SDL_WINDOW_OPENGL);
|
||||||
|
|
||||||
|
if (!win) {
|
||||||
|
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GLContext glContext = SDL_GL_CreateContext(win);
|
||||||
|
if (!glContext) {
|
||||||
|
std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GL_MakeCurrent(win, glContext);
|
||||||
|
ZL::Environment::window = win;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||||
|
|
||||||
|
|
||||||
|
ZL::Environment::window = SDL_CreateWindow(
|
||||||
|
"Space Ship Game",
|
||||||
|
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
ZL::Environment::width, ZL::Environment::height,
|
||||||
|
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||||||
|
);
|
||||||
|
#else
|
||||||
|
// Для десктопа
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||||
|
|
||||||
|
ZL::Environment::window = SDL_CreateWindow(
|
||||||
|
"Space Ship Game",
|
||||||
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
|
ZL::Environment::width, ZL::Environment::height,
|
||||||
|
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ZL::Environment::window) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Game", "Failed to create window: %s", SDL_GetError());
|
||||||
|
#else
|
||||||
|
SDL_Log("Failed to create window: %s", SDL_GetError());
|
||||||
|
#endif
|
||||||
|
SDL_Quit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
||||||
|
if (!ctx) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_CreateContext failed: %s", SDL_GetError());
|
||||||
|
#else
|
||||||
|
SDL_Log("SDL_GL_CreateContext failed: %s", SDL_GetError());
|
||||||
|
#endif
|
||||||
|
SDL_DestroyWindow(ZL::Environment::window);
|
||||||
|
SDL_Quit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SDL_GL_MakeCurrent(ZL::Environment::window, ctx) != 0) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Game", "SDL_GL_MakeCurrent failed: %s", SDL_GetError());
|
||||||
|
#else
|
||||||
|
SDL_Log("SDL_GL_MakeCurrent failed: %s", SDL_GetError());
|
||||||
|
#endif
|
||||||
|
SDL_GL_DeleteContext(ctx);
|
||||||
|
SDL_DestroyWindow(ZL::Environment::window);
|
||||||
|
SDL_Quit();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Настройка VSync
|
||||||
|
if (SDL_GL_SetSwapInterval(1) < 0) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, "Game", "Unable to set VSync: %s", SDL_GetError());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Настройка игры
|
||||||
|
try {
|
||||||
|
game.setup();
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
__android_log_print(ANDROID_LOG_ERROR, "Game", "Game setup failed: %s", e.what());
|
||||||
|
#else
|
||||||
|
std::cerr << "Game setup failed: " << e.what() << std::endl;
|
||||||
|
#endif
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef EMSCRIPTEN
|
||||||
|
emscripten_set_main_loop(MainLoop, 0, 1);
|
||||||
|
#else
|
||||||
|
bool running = true;
|
||||||
|
while (running && !game.shouldExit()) {
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (event.type == SDL_QUIT) {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_AC_BACK) {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
game.update();
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
SDL_Delay(16);
|
||||||
|
#else
|
||||||
|
SDL_Delay(2);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Vendored
+98
@@ -0,0 +1,98 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in [sdk]/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the proguardFiles
|
||||||
|
# directive in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLInputConnection {
|
||||||
|
void nativeCommitText(java.lang.String, int);
|
||||||
|
void nativeGenerateScancodeForUnichar(char);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses class org.libsdl.app.SDLActivity {
|
||||||
|
# for some reason these aren't compatible with allowoptimization modifier
|
||||||
|
boolean supportsRelativeMouse();
|
||||||
|
void setWindowStyle(boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {
|
||||||
|
java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it
|
||||||
|
boolean onNativeSoftReturnKey();
|
||||||
|
void onNativeKeyboardFocusLost();
|
||||||
|
boolean isScreenKeyboardShown();
|
||||||
|
android.util.DisplayMetrics getDisplayDPI();
|
||||||
|
java.lang.String clipboardGetText();
|
||||||
|
boolean clipboardHasText();
|
||||||
|
void clipboardSetText(java.lang.String);
|
||||||
|
int createCustomCursor(int[], int, int, int, int);
|
||||||
|
void destroyCustomCursor(int);
|
||||||
|
android.content.Context getContext();
|
||||||
|
boolean getManifestEnvironmentVariables();
|
||||||
|
android.view.Surface getNativeSurface();
|
||||||
|
void initTouch();
|
||||||
|
boolean isAndroidTV();
|
||||||
|
boolean isChromebook();
|
||||||
|
boolean isDeXMode();
|
||||||
|
boolean isTablet();
|
||||||
|
void manualBackButton();
|
||||||
|
int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]);
|
||||||
|
void minimizeWindow();
|
||||||
|
int openURL(java.lang.String);
|
||||||
|
void requestPermission(java.lang.String, int);
|
||||||
|
int showToast(java.lang.String, int, int, int, int);
|
||||||
|
boolean sendMessage(int, int);
|
||||||
|
boolean setActivityTitle(java.lang.String);
|
||||||
|
boolean setCustomCursor(int);
|
||||||
|
void setOrientation(int, int, boolean, java.lang.String);
|
||||||
|
boolean setRelativeMouseEnabled(boolean);
|
||||||
|
boolean setSystemCursor(int);
|
||||||
|
boolean shouldMinimizeOnFocusLoss();
|
||||||
|
boolean showTextInput(int, int, int, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager {
|
||||||
|
boolean initialize(boolean, boolean);
|
||||||
|
boolean openDevice(int);
|
||||||
|
int sendOutputReport(int, byte[]);
|
||||||
|
int sendFeatureReport(int, byte[]);
|
||||||
|
boolean getFeatureReport(int, byte[]);
|
||||||
|
void closeDevice(int);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager {
|
||||||
|
int[] getAudioOutputDevices();
|
||||||
|
int[] getAudioInputDevices();
|
||||||
|
int[] audioOpen(int, int, int, int, int);
|
||||||
|
void audioWriteFloatBuffer(float[]);
|
||||||
|
void audioWriteShortBuffer(short[]);
|
||||||
|
void audioWriteByteBuffer(byte[]);
|
||||||
|
void audioClose();
|
||||||
|
int[] captureOpen(int, int, int, int, int);
|
||||||
|
int captureReadFloatBuffer(float[], boolean);
|
||||||
|
int captureReadShortBuffer(short[], boolean);
|
||||||
|
int captureReadByteBuffer(byte[], boolean);
|
||||||
|
void captureClose();
|
||||||
|
void audioSetThreadPriority(boolean, int);
|
||||||
|
native int nativeSetupJNI();
|
||||||
|
native void removeAudioDevice(boolean, int);
|
||||||
|
native void addAudioDevice(boolean, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager {
|
||||||
|
void pollInputDevices();
|
||||||
|
void pollHapticDevices();
|
||||||
|
void hapticRun(int, float, int);
|
||||||
|
void hapticStop(int);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Replace com.test.game with the identifier of your game below, e.g.
|
||||||
|
com.gamemaker.game
|
||||||
|
-->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:versionCode="1"
|
||||||
|
android:versionName="1.0"
|
||||||
|
android:installLocation="auto">
|
||||||
|
|
||||||
|
<!-- OpenGL ES 2.0 -->
|
||||||
|
<uses-feature android:glEsVersion="0x00020000" />
|
||||||
|
|
||||||
|
<!-- Touchscreen support -->
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.touchscreen"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- Game controller support -->
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.bluetooth"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.gamepad"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.usb.host"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- External mouse input events -->
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.type.pc"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<!-- Audio recording support -->
|
||||||
|
<!-- if you want to capture audio, uncomment this. -->
|
||||||
|
<!-- <uses-feature
|
||||||
|
android:name="android.hardware.microphone"
|
||||||
|
android:required="false" /> -->
|
||||||
|
|
||||||
|
<!-- Allow downloading to the external storage on Android 5.1 and older -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="22" /> -->
|
||||||
|
|
||||||
|
<!-- Allow access to Bluetooth devices -->
|
||||||
|
<!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> -->
|
||||||
|
|
||||||
|
<!-- Allow access to the vibrator -->
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
|
||||||
|
<!-- if you want to capture audio, uncomment this. -->
|
||||||
|
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
|
||||||
|
|
||||||
|
<!-- Create a Java class extending SDLActivity and place it in a
|
||||||
|
directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
|
||||||
|
|
||||||
|
then replace "SDLActivity" with the name of your class (e.g. "MyGame")
|
||||||
|
in the XML below.
|
||||||
|
|
||||||
|
An example Java class can be found in README-android.md
|
||||||
|
-->
|
||||||
|
<application android:label="@string/app_name"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
android:hardwareAccelerated="true" >
|
||||||
|
|
||||||
|
<!-- Example of setting SDL hints from AndroidManifest.xml:
|
||||||
|
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<activity android:name="SDLActivity"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:alwaysRetainTaskState="true"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
|
||||||
|
android:preferMinimalPostProcessing="true"
|
||||||
|
android:exported="true"
|
||||||
|
>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- Let Android know that we can handle some USB devices and should receive this event -->
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||||
|
</intent-filter>
|
||||||
|
<!-- Drop file event -->
|
||||||
|
<!--
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<data android:mimeType="*/*" />
|
||||||
|
</intent-filter>
|
||||||
|
-->
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
File renamed without changes.
File renamed without changes.
Binary file not shown.
|
After Width: | Height: | Size: 4.0 MiB |
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,7 @@
|
|||||||
|
precision mediump float;
|
||||||
|
varying vec3 color;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,9 @@
|
|||||||
|
precision mediump float;
|
||||||
|
uniform sampler2D Texture;
|
||||||
|
varying vec2 texCoord;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 color = texture2D(Texture, texCoord);
|
||||||
|
gl_FragColor = color;
|
||||||
|
}
|
||||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,22 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
|
||||||
|
interface HIDDevice
|
||||||
|
{
|
||||||
|
public int getId();
|
||||||
|
public int getVendorId();
|
||||||
|
public int getProductId();
|
||||||
|
public String getSerialNumber();
|
||||||
|
public int getVersion();
|
||||||
|
public String getManufacturerName();
|
||||||
|
public String getProductName();
|
||||||
|
public UsbDevice getDevice();
|
||||||
|
public boolean open();
|
||||||
|
public int sendFeatureReport(byte[] report);
|
||||||
|
public int sendOutputReport(byte[] report);
|
||||||
|
public boolean getFeatureReport(byte[] report);
|
||||||
|
public void setFrozen(boolean frozen);
|
||||||
|
public void close();
|
||||||
|
public void shutdown();
|
||||||
|
}
|
||||||
@@ -0,0 +1,650 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothGatt;
|
||||||
|
import android.bluetooth.BluetoothGattCallback;
|
||||||
|
import android.bluetooth.BluetoothGattCharacteristic;
|
||||||
|
import android.bluetooth.BluetoothGattDescriptor;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.bluetooth.BluetoothGattService;
|
||||||
|
import android.hardware.usb.UsbDevice;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.os.*;
|
||||||
|
|
||||||
|
//import com.android.internal.util.HexDump;
|
||||||
|
|
||||||
|
import java.lang.Runnable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
|
||||||
|
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
private HIDDeviceManager mManager;
|
||||||
|
private BluetoothDevice mDevice;
|
||||||
|
private int mDeviceId;
|
||||||
|
private BluetoothGatt mGatt;
|
||||||
|
private boolean mIsRegistered = false;
|
||||||
|
private boolean mIsConnected = false;
|
||||||
|
private boolean mIsChromebook = false;
|
||||||
|
private boolean mIsReconnecting = false;
|
||||||
|
private boolean mFrozen = false;
|
||||||
|
private LinkedList<GattOperation> mOperations;
|
||||||
|
GattOperation mCurrentOperation = null;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
|
private static final int TRANSPORT_AUTO = 0;
|
||||||
|
private static final int TRANSPORT_BREDR = 1;
|
||||||
|
private static final int TRANSPORT_LE = 2;
|
||||||
|
|
||||||
|
private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
|
||||||
|
|
||||||
|
static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
|
||||||
|
static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
|
||||||
|
static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
|
||||||
|
static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
|
||||||
|
|
||||||
|
static class GattOperation {
|
||||||
|
private enum Operation {
|
||||||
|
CHR_READ,
|
||||||
|
CHR_WRITE,
|
||||||
|
ENABLE_NOTIFICATION
|
||||||
|
}
|
||||||
|
|
||||||
|
Operation mOp;
|
||||||
|
UUID mUuid;
|
||||||
|
byte[] mValue;
|
||||||
|
BluetoothGatt mGatt;
|
||||||
|
boolean mResult = true;
|
||||||
|
|
||||||
|
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
|
||||||
|
mGatt = gatt;
|
||||||
|
mOp = operation;
|
||||||
|
mUuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
|
||||||
|
mGatt = gatt;
|
||||||
|
mOp = operation;
|
||||||
|
mUuid = uuid;
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
// This is executed in main thread
|
||||||
|
BluetoothGattCharacteristic chr;
|
||||||
|
|
||||||
|
switch (mOp) {
|
||||||
|
case CHR_READ:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Reading characteristic " + chr.getUuid());
|
||||||
|
if (!mGatt.readCharacteristic(chr)) {
|
||||||
|
Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
break;
|
||||||
|
case CHR_WRITE:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
|
||||||
|
chr.setValue(mValue);
|
||||||
|
if (!mGatt.writeCharacteristic(chr)) {
|
||||||
|
Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
break;
|
||||||
|
case ENABLE_NOTIFICATION:
|
||||||
|
chr = getCharacteristic(mUuid);
|
||||||
|
//Log.v(TAG, "Writing descriptor of " + chr.getUuid());
|
||||||
|
if (chr != null) {
|
||||||
|
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||||
|
if (cccd != null) {
|
||||||
|
int properties = chr.getProperties();
|
||||||
|
byte[] value;
|
||||||
|
if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
|
||||||
|
value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||||
|
} else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
|
||||||
|
value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Unable to start notifications on input characteristic");
|
||||||
|
mResult = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mGatt.setCharacteristicNotification(chr, true);
|
||||||
|
cccd.setValue(value);
|
||||||
|
if (!mGatt.writeDescriptor(cccd)) {
|
||||||
|
Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
|
||||||
|
mResult = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mResult = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean finish() {
|
||||||
|
return mResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
|
||||||
|
BluetoothGattService valveService = mGatt.getService(steamControllerService);
|
||||||
|
if (valveService == null)
|
||||||
|
return null;
|
||||||
|
return valveService.getCharacteristic(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
|
||||||
|
return new GattOperation(gatt, Operation.CHR_READ, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
|
||||||
|
return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
|
||||||
|
return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
|
||||||
|
mManager = manager;
|
||||||
|
mDevice = device;
|
||||||
|
mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
|
||||||
|
mIsRegistered = false;
|
||||||
|
mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||||
|
mOperations = new LinkedList<GattOperation>();
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
|
mGatt = connectGatt();
|
||||||
|
// final HIDDeviceBLESteamController finalThis = this;
|
||||||
|
// mHandler.postDelayed(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// finalThis.checkConnectionForChromebookIssue();
|
||||||
|
// }
|
||||||
|
// }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("SteamController.%s", mDevice.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BluetoothGatt getGatt() {
|
||||||
|
return mGatt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
|
||||||
|
// of TRANSPORT_LE. Let's force ourselves to connect low energy.
|
||||||
|
private BluetoothGatt connectGatt(boolean managed) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||||
|
try {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return mDevice.connectGatt(mManager.getContext(), managed, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BluetoothGatt connectGatt() {
|
||||||
|
return connectGatt(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getConnectionState() {
|
||||||
|
|
||||||
|
Context context = mManager.getContext();
|
||||||
|
if (context == null) {
|
||||||
|
// We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
|
||||||
|
return BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||||
|
if (btManager == null) {
|
||||||
|
// This device doesn't support Bluetooth. We should never be here, because how did
|
||||||
|
// we instantiate a device to start with?
|
||||||
|
return BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reconnect() {
|
||||||
|
|
||||||
|
if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void checkConnectionForChromebookIssue() {
|
||||||
|
if (!mIsChromebook) {
|
||||||
|
// We only do this on Chromebooks, because otherwise it's really annoying to just attempt
|
||||||
|
// over and over.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int connectionState = getConnectionState();
|
||||||
|
|
||||||
|
switch (connectionState) {
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
if (!mIsConnected) {
|
||||||
|
// We are in the Bad Chromebook Place. We can force a disconnect
|
||||||
|
// to try to recover.
|
||||||
|
Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!isRegistered()) {
|
||||||
|
if (mGatt.getServices().size() > 0) {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
|
||||||
|
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTING:
|
||||||
|
Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final HIDDeviceBLESteamController finalThis = this;
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finalThis.checkConnectionForChromebookIssue();
|
||||||
|
}
|
||||||
|
}, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isRegistered() {
|
||||||
|
return mIsRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRegistered() {
|
||||||
|
mIsRegistered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean probeService(HIDDeviceBLESteamController controller) {
|
||||||
|
|
||||||
|
if (isRegistered()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIsConnected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v(TAG, "probeService controller=" + controller);
|
||||||
|
|
||||||
|
for (BluetoothGattService service : mGatt.getServices()) {
|
||||||
|
if (service.getUuid().equals(steamControllerService)) {
|
||||||
|
Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
|
||||||
|
|
||||||
|
for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
|
||||||
|
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||||
|
Log.v(TAG, "Found input characteristic");
|
||||||
|
// Start notifications
|
||||||
|
BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
|
||||||
|
if (cccd != null) {
|
||||||
|
enableNotification(chr.getUuid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
|
||||||
|
Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
|
||||||
|
mIsConnected = false;
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mGatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private void finishCurrentGattOperation() {
|
||||||
|
GattOperation op = null;
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation != null) {
|
||||||
|
op = mCurrentOperation;
|
||||||
|
mCurrentOperation = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op != null) {
|
||||||
|
boolean result = op.finish(); // TODO: Maybe in main thread as well?
|
||||||
|
|
||||||
|
// Our operation failed, let's add it back to the beginning of our queue.
|
||||||
|
if (!result) {
|
||||||
|
mOperations.addFirst(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
executeNextGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void executeNextGattOperation() {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mOperations.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mCurrentOperation = mOperations.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run in main thread
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
if (mCurrentOperation == null) {
|
||||||
|
Log.e(TAG, "Current operation null in executor?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mCurrentOperation.run();
|
||||||
|
// now wait for the GATT callback and when it comes, finish this operation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queueGattOperation(GattOperation op) {
|
||||||
|
synchronized (mOperations) {
|
||||||
|
mOperations.add(op);
|
||||||
|
}
|
||||||
|
executeNextGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableNotification(UUID chrUuid) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeCharacteristic(UUID uuid, byte[] value) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readCharacteristic(UUID uuid) {
|
||||||
|
GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
|
||||||
|
queueGattOperation(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////// BluetoothGattCallback overridden methods
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
|
||||||
|
//Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
|
||||||
|
mIsReconnecting = false;
|
||||||
|
if (newState == 2) {
|
||||||
|
mIsConnected = true;
|
||||||
|
// Run directly, without GattOperation
|
||||||
|
if (!isRegistered()) {
|
||||||
|
mHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
mGatt.discoverServices();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (newState == 0) {
|
||||||
|
mIsConnected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
|
||||||
|
//Log.v(TAG, "onServicesDiscovered status=" + status);
|
||||||
|
if (status == 0) {
|
||||||
|
if (gatt.getServices().size() == 0) {
|
||||||
|
Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
|
||||||
|
mIsReconnecting = true;
|
||||||
|
mIsConnected = false;
|
||||||
|
gatt.disconnect();
|
||||||
|
mGatt = connectGatt(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
//Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
|
||||||
|
mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
|
||||||
|
//Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(reportCharacteristic)) {
|
||||||
|
// Only register controller with the native side once it has been fully configured
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.v(TAG, "Registering Steam Controller with ID: " + getId());
|
||||||
|
mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
|
||||||
|
setRegistered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||||
|
// Enable this for verbose logging of controller input reports
|
||||||
|
//Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
|
||||||
|
|
||||||
|
if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
|
||||||
|
mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
//Log.v(TAG, "onDescriptorRead status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
|
||||||
|
BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
|
||||||
|
//Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
|
||||||
|
|
||||||
|
if (chr.getUuid().equals(inputCharacteristic)) {
|
||||||
|
boolean hasWrittenInputDescriptor = true;
|
||||||
|
BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
|
||||||
|
if (reportChr != null) {
|
||||||
|
Log.v(TAG, "Writing report characteristic to enter valve mode");
|
||||||
|
reportChr.setValue(enterValveMode);
|
||||||
|
gatt.writeCharacteristic(reportChr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
finishCurrentGattOperation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
|
||||||
|
//Log.v(TAG, "onReliableWriteCompleted status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
|
||||||
|
//Log.v(TAG, "onReadRemoteRssi status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
|
||||||
|
//Log.v(TAG, "onMtuChanged status=" + status);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////// Public API
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return mDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId() {
|
||||||
|
// Valve Corporation
|
||||||
|
final int VALVE_USB_VID = 0x28DE;
|
||||||
|
return VALVE_USB_VID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId() {
|
||||||
|
// We don't have an easy way to query from the Bluetooth device, but we know what it is
|
||||||
|
final int D0G_BLE2_PID = 0x1106;
|
||||||
|
return D0G_BLE2_PID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSerialNumber() {
|
||||||
|
// This will be read later via feature report by Steam
|
||||||
|
return "12345";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getManufacturerName() {
|
||||||
|
return "Valve Corporation";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProductName() {
|
||||||
|
return "Steam Controller";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsbDevice getDevice() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean open() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendFeatureReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to skip the first byte, as that doesn't go over the air
|
||||||
|
byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
|
||||||
|
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
|
||||||
|
writeCharacteristic(reportCharacteristic, actual_report);
|
||||||
|
return report.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendOutputReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
|
||||||
|
writeCharacteristic(reportCharacteristic, report);
|
||||||
|
return report.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getFeatureReport(byte[] report) {
|
||||||
|
if (!isRegistered()) {
|
||||||
|
Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
|
||||||
|
if (mIsConnected) {
|
||||||
|
probeService(this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Log.v(TAG, "getFeatureReport");
|
||||||
|
readCharacteristic(reportCharacteristic);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
mFrozen = frozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
close();
|
||||||
|
|
||||||
|
BluetoothGatt g = mGatt;
|
||||||
|
if (g != null) {
|
||||||
|
g.disconnect();
|
||||||
|
g.close();
|
||||||
|
mGatt = null;
|
||||||
|
}
|
||||||
|
mManager = null;
|
||||||
|
mIsRegistered = false;
|
||||||
|
mIsConnected = false;
|
||||||
|
mOperations.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,698 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothManager;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HIDDeviceManager {
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
|
||||||
|
|
||||||
|
private static HIDDeviceManager sManager;
|
||||||
|
private static int sManagerRefCount = 0;
|
||||||
|
|
||||||
|
public static HIDDeviceManager acquire(Context context) {
|
||||||
|
if (sManagerRefCount == 0) {
|
||||||
|
sManager = new HIDDeviceManager(context);
|
||||||
|
}
|
||||||
|
++sManagerRefCount;
|
||||||
|
return sManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void release(HIDDeviceManager manager) {
|
||||||
|
if (manager == sManager) {
|
||||||
|
--sManagerRefCount;
|
||||||
|
if (sManagerRefCount == 0) {
|
||||||
|
sManager.close();
|
||||||
|
sManager = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private HashMap<Integer, HIDDevice> mDevicesById = new HashMap<Integer, HIDDevice>();
|
||||||
|
private HashMap<BluetoothDevice, HIDDeviceBLESteamController> mBluetoothDevices = new HashMap<BluetoothDevice, HIDDeviceBLESteamController>();
|
||||||
|
private int mNextDeviceId = 0;
|
||||||
|
private SharedPreferences mSharedPreferences = null;
|
||||||
|
private boolean mIsChromebook = false;
|
||||||
|
private UsbManager mUsbManager;
|
||||||
|
private Handler mHandler;
|
||||||
|
private BluetoothManager mBluetoothManager;
|
||||||
|
private List<BluetoothDevice> mLastBluetoothDevices;
|
||||||
|
|
||||||
|
private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDeviceAttached(usbDevice);
|
||||||
|
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDeviceDetached(usbDevice);
|
||||||
|
} else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
|
||||||
|
UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||||
|
handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
// Bluetooth device was connected. If it was a Steam Controller, handle it
|
||||||
|
if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
Log.d(TAG, "Bluetooth device connected: " + device);
|
||||||
|
|
||||||
|
if (isSteamController(device)) {
|
||||||
|
connectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bluetooth device was disconnected, remove from controller manager (if any)
|
||||||
|
if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
|
||||||
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
Log.d(TAG, "Bluetooth device disconnected: " + device);
|
||||||
|
|
||||||
|
disconnectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private HIDDeviceManager(final Context context) {
|
||||||
|
mContext = context;
|
||||||
|
|
||||||
|
HIDDeviceRegisterCallback();
|
||||||
|
|
||||||
|
mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
|
||||||
|
mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
|
||||||
|
|
||||||
|
// if (shouldClear) {
|
||||||
|
// SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||||
|
// spedit.clear();
|
||||||
|
// spedit.commit();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
{
|
||||||
|
mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeviceIDForIdentifier(String identifier) {
|
||||||
|
SharedPreferences.Editor spedit = mSharedPreferences.edit();
|
||||||
|
|
||||||
|
int result = mSharedPreferences.getInt(identifier, 0);
|
||||||
|
if (result == 0) {
|
||||||
|
result = mNextDeviceId++;
|
||||||
|
spedit.putInt("next_device_id", mNextDeviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
spedit.putInt(identifier, result);
|
||||||
|
spedit.commit();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeUSB() {
|
||||||
|
mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
|
||||||
|
if (mUsbManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Logging
|
||||||
|
for (UsbDevice device : mUsbManager.getDeviceList().values()) {
|
||||||
|
Log.i(TAG,"Path: " + device.getDeviceName());
|
||||||
|
Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
|
||||||
|
Log.i(TAG,"Product: " + device.getProductName());
|
||||||
|
Log.i(TAG,"ID: " + device.getDeviceId());
|
||||||
|
Log.i(TAG,"Class: " + device.getDeviceClass());
|
||||||
|
Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
|
||||||
|
Log.i(TAG,"Vendor ID " + device.getVendorId());
|
||||||
|
Log.i(TAG,"Product ID: " + device.getProductId());
|
||||||
|
Log.i(TAG,"Interface count: " + device.getInterfaceCount());
|
||||||
|
Log.i(TAG,"---------------------------------------");
|
||||||
|
|
||||||
|
// Get interface details
|
||||||
|
for (int index = 0; index < device.getInterfaceCount(); index++) {
|
||||||
|
UsbInterface mUsbInterface = device.getInterface(index);
|
||||||
|
Log.i(TAG," ***** *****");
|
||||||
|
Log.i(TAG," Interface index: " + index);
|
||||||
|
Log.i(TAG," Interface ID: " + mUsbInterface.getId());
|
||||||
|
Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
|
||||||
|
Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
|
||||||
|
Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
|
||||||
|
Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
|
||||||
|
|
||||||
|
// Get endpoint details
|
||||||
|
for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
|
||||||
|
{
|
||||||
|
UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
|
||||||
|
Log.i(TAG," ++++ ++++ ++++");
|
||||||
|
Log.i(TAG," Endpoint index: " + epi);
|
||||||
|
Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
|
||||||
|
Log.i(TAG," Direction: " + mEndpoint.getDirection());
|
||||||
|
Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
|
||||||
|
Log.i(TAG," Interval: " + mEndpoint.getInterval());
|
||||||
|
Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
|
||||||
|
Log.i(TAG," Type: " + mEndpoint.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(TAG," No more devices connected.");
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Register for USB broadcasts and permission completions
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
||||||
|
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
||||||
|
filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||||
|
mContext.registerReceiver(mUsbBroadcast, filter);
|
||||||
|
|
||||||
|
for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
|
||||||
|
handleUsbDeviceAttached(usbDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UsbManager getUSBManager() {
|
||||||
|
return mUsbManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownUSB() {
|
||||||
|
try {
|
||||||
|
mContext.unregisterReceiver(mUsbBroadcast);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We may not have registered, that's okay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
final int XB360_IFACE_SUBCLASS = 93;
|
||||||
|
final int XB360_IFACE_PROTOCOL = 1; // Wired
|
||||||
|
final int XB360W_IFACE_PROTOCOL = 129; // Wireless
|
||||||
|
final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x0079, // GPD Win 2
|
||||||
|
0x044f, // Thrustmaster
|
||||||
|
0x045e, // Microsoft
|
||||||
|
0x046d, // Logitech
|
||||||
|
0x056e, // Elecom
|
||||||
|
0x06a3, // Saitek
|
||||||
|
0x0738, // Mad Catz
|
||||||
|
0x07ff, // Mad Catz
|
||||||
|
0x0e6f, // PDP
|
||||||
|
0x0f0d, // Hori
|
||||||
|
0x1038, // SteelSeries
|
||||||
|
0x11c9, // Nacon
|
||||||
|
0x12ab, // Unknown
|
||||||
|
0x1430, // RedOctane
|
||||||
|
0x146b, // BigBen
|
||||||
|
0x1532, // Razer Sabertooth
|
||||||
|
0x15e4, // Numark
|
||||||
|
0x162e, // Joytech
|
||||||
|
0x1689, // Razer Onza
|
||||||
|
0x1949, // Lab126, Inc.
|
||||||
|
0x1bad, // Harmonix
|
||||||
|
0x20d6, // PowerA
|
||||||
|
0x24c6, // PowerA
|
||||||
|
0x2c22, // Qanba
|
||||||
|
0x2dc8, // 8BitDo
|
||||||
|
0x9886, // ASTRO Gaming
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
|
||||||
|
(usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
|
||||||
|
usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
|
||||||
|
int vendor_id = usbDevice.getVendorId();
|
||||||
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
|
if (vendor_id == supportedVid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
|
||||||
|
final int XB1_IFACE_SUBCLASS = 71;
|
||||||
|
final int XB1_IFACE_PROTOCOL = 208;
|
||||||
|
final int[] SUPPORTED_VENDORS = {
|
||||||
|
0x03f0, // HP
|
||||||
|
0x044f, // Thrustmaster
|
||||||
|
0x045e, // Microsoft
|
||||||
|
0x0738, // Mad Catz
|
||||||
|
0x0b05, // ASUS
|
||||||
|
0x0e6f, // PDP
|
||||||
|
0x0f0d, // Hori
|
||||||
|
0x10f5, // Turtle Beach
|
||||||
|
0x1532, // Razer Wildcat
|
||||||
|
0x20d6, // PowerA
|
||||||
|
0x24c6, // PowerA
|
||||||
|
0x2dc8, // 8BitDo
|
||||||
|
0x2e24, // Hyperkin
|
||||||
|
0x3537, // GameSir
|
||||||
|
};
|
||||||
|
|
||||||
|
if (usbInterface.getId() == 0 &&
|
||||||
|
usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
|
||||||
|
usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
|
||||||
|
usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
|
||||||
|
int vendor_id = usbDevice.getVendorId();
|
||||||
|
for (int supportedVid : SUPPORTED_VENDORS) {
|
||||||
|
if (vendor_id == supportedVid) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDeviceAttached(UsbDevice usbDevice) {
|
||||||
|
connectHIDDeviceUSB(usbDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDeviceDetached(UsbDevice usbDevice) {
|
||||||
|
List<Integer> devices = new ArrayList<Integer>();
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
if (usbDevice.equals(device.getDevice())) {
|
||||||
|
devices.add(device.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int id : devices) {
|
||||||
|
HIDDevice device = mDevicesById.get(id);
|
||||||
|
mDevicesById.remove(id);
|
||||||
|
device.shutdown();
|
||||||
|
HIDDeviceDisconnected(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
if (usbDevice.equals(device.getDevice())) {
|
||||||
|
boolean opened = false;
|
||||||
|
if (permission_granted) {
|
||||||
|
opened = device.open();
|
||||||
|
}
|
||||||
|
HIDDeviceOpenResult(device.getId(), opened);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectHIDDeviceUSB(UsbDevice usbDevice) {
|
||||||
|
synchronized (this) {
|
||||||
|
int interface_mask = 0;
|
||||||
|
for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
|
||||||
|
UsbInterface usbInterface = usbDevice.getInterface(interface_index);
|
||||||
|
if (isHIDDeviceInterface(usbDevice, usbInterface)) {
|
||||||
|
// Check to see if we've already added this interface
|
||||||
|
// This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
|
||||||
|
int interface_id = usbInterface.getId();
|
||||||
|
if ((interface_mask & (1 << interface_id)) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
interface_mask |= (1 << interface_id);
|
||||||
|
|
||||||
|
HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
|
||||||
|
int id = device.getId();
|
||||||
|
mDevicesById.put(id, device);
|
||||||
|
HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeBluetooth() {
|
||||||
|
Log.d(TAG, "Initializing Bluetooth");
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 31 /* Android 12 */ &&
|
||||||
|
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH_CONNECT, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH_CONNECT");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT <= 30 /* Android 11.0 (R) */ &&
|
||||||
|
mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18 /* Android 4.3 (JELLY_BEAN_MR2) */)) {
|
||||||
|
Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find bonded bluetooth controllers and create SteamControllers for them
|
||||||
|
mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||||
|
if (mBluetoothManager == null) {
|
||||||
|
// This device doesn't support Bluetooth.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
|
||||||
|
if (btAdapter == null) {
|
||||||
|
// This device has Bluetooth support in the codebase, but has no available adapters.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get our bonded devices.
|
||||||
|
for (BluetoothDevice device : btAdapter.getBondedDevices()) {
|
||||||
|
|
||||||
|
Log.d(TAG, "Bluetooth device available: " + device);
|
||||||
|
if (isSteamController(device)) {
|
||||||
|
connectBluetoothDevice(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: These don't work on Chromebooks, to my undying dismay.
|
||||||
|
IntentFilter filter = new IntentFilter();
|
||||||
|
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||||
|
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||||
|
mContext.registerReceiver(mBluetoothBroadcast, filter);
|
||||||
|
|
||||||
|
if (mIsChromebook) {
|
||||||
|
mHandler = new Handler(Looper.getMainLooper());
|
||||||
|
mLastBluetoothDevices = new ArrayList<BluetoothDevice>();
|
||||||
|
|
||||||
|
// final HIDDeviceManager finalThis = this;
|
||||||
|
// mHandler.postDelayed(new Runnable() {
|
||||||
|
// @Override
|
||||||
|
// public void run() {
|
||||||
|
// finalThis.chromebookConnectionHandler();
|
||||||
|
// }
|
||||||
|
// }, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void shutdownBluetooth() {
|
||||||
|
try {
|
||||||
|
mContext.unregisterReceiver(mBluetoothBroadcast);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// We may not have registered, that's okay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
|
||||||
|
// This function provides a sort of dummy version of that, watching for changes in the
|
||||||
|
// connected devices and attempting to add controllers as things change.
|
||||||
|
public void chromebookConnectionHandler() {
|
||||||
|
if (!mIsChromebook) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<BluetoothDevice> disconnected = new ArrayList<BluetoothDevice>();
|
||||||
|
ArrayList<BluetoothDevice> connected = new ArrayList<BluetoothDevice>();
|
||||||
|
|
||||||
|
List<BluetoothDevice> currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
|
||||||
|
|
||||||
|
for (BluetoothDevice bluetoothDevice : currentConnected) {
|
||||||
|
if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
|
||||||
|
connected.add(bluetoothDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
|
||||||
|
if (!currentConnected.contains(bluetoothDevice)) {
|
||||||
|
disconnected.add(bluetoothDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastBluetoothDevices = currentConnected;
|
||||||
|
|
||||||
|
for (BluetoothDevice bluetoothDevice : disconnected) {
|
||||||
|
disconnectBluetoothDevice(bluetoothDevice);
|
||||||
|
}
|
||||||
|
for (BluetoothDevice bluetoothDevice : connected) {
|
||||||
|
connectBluetoothDevice(bluetoothDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
final HIDDeviceManager finalThis = this;
|
||||||
|
mHandler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
finalThis.chromebookConnectionHandler();
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||||
|
Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
|
||||||
|
synchronized (this) {
|
||||||
|
if (mBluetoothDevices.containsKey(bluetoothDevice)) {
|
||||||
|
Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
|
||||||
|
|
||||||
|
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||||
|
device.reconnect();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
|
||||||
|
int id = device.getId();
|
||||||
|
mBluetoothDevices.put(bluetoothDevice, device);
|
||||||
|
mDevicesById.put(id, device);
|
||||||
|
|
||||||
|
// The Steam Controller will mark itself connected once initialization is complete
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
|
||||||
|
synchronized (this) {
|
||||||
|
HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
|
||||||
|
if (device == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int id = device.getId();
|
||||||
|
mBluetoothDevices.remove(bluetoothDevice);
|
||||||
|
mDevicesById.remove(id);
|
||||||
|
device.shutdown();
|
||||||
|
HIDDeviceDisconnected(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSteamController(BluetoothDevice bluetoothDevice) {
|
||||||
|
// Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
|
||||||
|
if (bluetoothDevice == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the device has no local name, we really don't want to try an equality check against it.
|
||||||
|
if (bluetoothDevice.getName() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void close() {
|
||||||
|
shutdownUSB();
|
||||||
|
shutdownBluetooth();
|
||||||
|
synchronized (this) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
device.shutdown();
|
||||||
|
}
|
||||||
|
mDevicesById.clear();
|
||||||
|
mBluetoothDevices.clear();
|
||||||
|
HIDDeviceReleaseCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
synchronized (this) {
|
||||||
|
for (HIDDevice device : mDevicesById.values()) {
|
||||||
|
device.setFrozen(frozen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private HIDDevice getDevice(int id) {
|
||||||
|
synchronized (this) {
|
||||||
|
HIDDevice result = mDevicesById.get(id);
|
||||||
|
if (result == null) {
|
||||||
|
Log.v(TAG, "No device for id: " + id);
|
||||||
|
Log.v(TAG, "Available devices: " + mDevicesById.keySet());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////// JNI interface functions
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public boolean initialize(boolean usb, boolean bluetooth) {
|
||||||
|
Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
|
||||||
|
|
||||||
|
if (usb) {
|
||||||
|
initializeUSB();
|
||||||
|
}
|
||||||
|
if (bluetooth) {
|
||||||
|
initializeBluetooth();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean openDevice(int deviceID) {
|
||||||
|
Log.v(TAG, "openDevice deviceID=" + deviceID);
|
||||||
|
HIDDevice device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look to see if this is a USB device and we have permission to access it
|
||||||
|
UsbDevice usbDevice = device.getDevice();
|
||||||
|
if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
|
||||||
|
HIDDeviceOpenPending(deviceID);
|
||||||
|
try {
|
||||||
|
final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
|
||||||
|
int flags;
|
||||||
|
if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) {
|
||||||
|
flags = FLAG_MUTABLE;
|
||||||
|
} else {
|
||||||
|
flags = 0;
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= 33 /* Android 14.0 (U) */) {
|
||||||
|
Intent intent = new Intent(HIDDeviceManager.ACTION_USB_PERMISSION);
|
||||||
|
intent.setPackage(mContext.getPackageName());
|
||||||
|
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, intent, flags));
|
||||||
|
} else {
|
||||||
|
mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
|
||||||
|
HIDDeviceOpenResult(deviceID, false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return device.open();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sendOutputReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.sendOutputReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int sendFeatureReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.sendFeatureReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getFeatureReport(int deviceID, byte[] report) {
|
||||||
|
try {
|
||||||
|
//Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return device.getFeatureReport(report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void closeDevice(int deviceID) {
|
||||||
|
try {
|
||||||
|
Log.v(TAG, "closeDevice deviceID=" + deviceID);
|
||||||
|
HIDDevice device;
|
||||||
|
device = getDevice(deviceID);
|
||||||
|
if (device == null) {
|
||||||
|
HIDDeviceDisconnected(deviceID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
device.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/////////////// Native methods
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private native void HIDDeviceRegisterCallback();
|
||||||
|
private native void HIDDeviceReleaseCallback();
|
||||||
|
|
||||||
|
native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
|
||||||
|
native void HIDDeviceOpenPending(int deviceID);
|
||||||
|
native void HIDDeviceOpenResult(int deviceID, boolean opened);
|
||||||
|
native void HIDDeviceDisconnected(int deviceID);
|
||||||
|
|
||||||
|
native void HIDDeviceInputReport(int deviceID, byte[] report);
|
||||||
|
native void HIDDeviceFeatureReport(int deviceID, byte[] report);
|
||||||
|
}
|
||||||
@@ -0,0 +1,309 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.hardware.usb.*;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
class HIDDeviceUSB implements HIDDevice {
|
||||||
|
|
||||||
|
private static final String TAG = "hidapi";
|
||||||
|
|
||||||
|
protected HIDDeviceManager mManager;
|
||||||
|
protected UsbDevice mDevice;
|
||||||
|
protected int mInterfaceIndex;
|
||||||
|
protected int mInterface;
|
||||||
|
protected int mDeviceId;
|
||||||
|
protected UsbDeviceConnection mConnection;
|
||||||
|
protected UsbEndpoint mInputEndpoint;
|
||||||
|
protected UsbEndpoint mOutputEndpoint;
|
||||||
|
protected InputThread mInputThread;
|
||||||
|
protected boolean mRunning;
|
||||||
|
protected boolean mFrozen;
|
||||||
|
|
||||||
|
public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
|
||||||
|
mManager = manager;
|
||||||
|
mDevice = usbDevice;
|
||||||
|
mInterfaceIndex = interface_index;
|
||||||
|
mInterface = mDevice.getInterface(mInterfaceIndex).getId();
|
||||||
|
mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
|
||||||
|
mRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIdentifier() {
|
||||||
|
return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getId() {
|
||||||
|
return mDeviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId() {
|
||||||
|
return mDevice.getVendorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId() {
|
||||||
|
return mDevice.getProductId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSerialNumber() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
try {
|
||||||
|
result = mDevice.getSerialNumber();
|
||||||
|
}
|
||||||
|
catch (SecurityException exception) {
|
||||||
|
//Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = "";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getManufacturerName() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
result = mDevice.getManufacturerName();
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = String.format("%x", getVendorId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getProductName() {
|
||||||
|
String result = null;
|
||||||
|
if (Build.VERSION.SDK_INT >= 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
result = mDevice.getProductName();
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = String.format("%x", getProductId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UsbDevice getDevice() {
|
||||||
|
return mDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDeviceName() {
|
||||||
|
return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean open() {
|
||||||
|
mConnection = mManager.getUSBManager().openDevice(mDevice);
|
||||||
|
if (mConnection == null) {
|
||||||
|
Log.w(TAG, "Unable to open USB device " + getDeviceName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force claim our interface
|
||||||
|
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||||
|
if (!mConnection.claimInterface(iface, true)) {
|
||||||
|
Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the endpoints
|
||||||
|
for (int j = 0; j < iface.getEndpointCount(); j++) {
|
||||||
|
UsbEndpoint endpt = iface.getEndpoint(j);
|
||||||
|
switch (endpt.getDirection()) {
|
||||||
|
case UsbConstants.USB_DIR_IN:
|
||||||
|
if (mInputEndpoint == null) {
|
||||||
|
mInputEndpoint = endpt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UsbConstants.USB_DIR_OUT:
|
||||||
|
if (mOutputEndpoint == null) {
|
||||||
|
mOutputEndpoint = endpt;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the required endpoints were present
|
||||||
|
if (mInputEndpoint == null || mOutputEndpoint == null) {
|
||||||
|
Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start listening for input
|
||||||
|
mRunning = true;
|
||||||
|
mInputThread = new InputThread();
|
||||||
|
mInputThread.start();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendFeatureReport(byte[] report) {
|
||||||
|
int res = -1;
|
||||||
|
int offset = 0;
|
||||||
|
int length = report.length;
|
||||||
|
boolean skipped_report_id = false;
|
||||||
|
byte report_number = report[0];
|
||||||
|
|
||||||
|
if (report_number == 0x0) {
|
||||||
|
++offset;
|
||||||
|
--length;
|
||||||
|
skipped_report_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = mConnection.controlTransfer(
|
||||||
|
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
|
||||||
|
0x09/*HID set_report*/,
|
||||||
|
(3/*HID feature*/ << 8) | report_number,
|
||||||
|
mInterface,
|
||||||
|
report, offset, length,
|
||||||
|
1000/*timeout millis*/);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped_report_id) {
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendOutputReport(byte[] report) {
|
||||||
|
int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
|
||||||
|
if (r != report.length) {
|
||||||
|
Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getFeatureReport(byte[] report) {
|
||||||
|
int res = -1;
|
||||||
|
int offset = 0;
|
||||||
|
int length = report.length;
|
||||||
|
boolean skipped_report_id = false;
|
||||||
|
byte report_number = report[0];
|
||||||
|
|
||||||
|
if (report_number == 0x0) {
|
||||||
|
/* Offset the return buffer by 1, so that the report ID
|
||||||
|
will remain in byte 0. */
|
||||||
|
++offset;
|
||||||
|
--length;
|
||||||
|
skipped_report_id = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = mConnection.controlTransfer(
|
||||||
|
UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
|
||||||
|
0x01/*HID get_report*/,
|
||||||
|
(3/*HID feature*/ << 8) | report_number,
|
||||||
|
mInterface,
|
||||||
|
report, offset, length,
|
||||||
|
1000/*timeout millis*/);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skipped_report_id) {
|
||||||
|
++res;
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] data;
|
||||||
|
if (res == length) {
|
||||||
|
data = report;
|
||||||
|
} else {
|
||||||
|
data = Arrays.copyOfRange(report, 0, res);
|
||||||
|
}
|
||||||
|
mManager.HIDDeviceFeatureReport(mDeviceId, data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mRunning = false;
|
||||||
|
if (mInputThread != null) {
|
||||||
|
while (mInputThread.isAlive()) {
|
||||||
|
mInputThread.interrupt();
|
||||||
|
try {
|
||||||
|
mInputThread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Keep trying until we're done
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mInputThread = null;
|
||||||
|
}
|
||||||
|
if (mConnection != null) {
|
||||||
|
UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
|
||||||
|
mConnection.releaseInterface(iface);
|
||||||
|
mConnection.close();
|
||||||
|
mConnection = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
close();
|
||||||
|
mManager = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrozen(boolean frozen) {
|
||||||
|
mFrozen = frozen;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class InputThread extends Thread {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
int packetSize = mInputEndpoint.getMaxPacketSize();
|
||||||
|
byte[] packet = new byte[packetSize];
|
||||||
|
while (mRunning) {
|
||||||
|
int r;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
// Could be a timeout or an I/O error
|
||||||
|
}
|
||||||
|
if (r > 0) {
|
||||||
|
byte[] data;
|
||||||
|
if (r == packetSize) {
|
||||||
|
data = packet;
|
||||||
|
} else {
|
||||||
|
data = Arrays.copyOfRange(packet, 0, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mFrozen) {
|
||||||
|
mManager.HIDDeviceInputReport(mDeviceId, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
SDL library initialization
|
||||||
|
*/
|
||||||
|
public class SDL {
|
||||||
|
|
||||||
|
// This function should be called first and sets up the native code
|
||||||
|
// so it can call into the Java classes
|
||||||
|
public static void setupJNI() {
|
||||||
|
SDLActivity.nativeSetupJNI();
|
||||||
|
SDLAudioManager.nativeSetupJNI();
|
||||||
|
SDLControllerManager.nativeSetupJNI();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should be called each time the activity is started
|
||||||
|
public static void initialize() {
|
||||||
|
setContext(null);
|
||||||
|
|
||||||
|
SDLActivity.initialize();
|
||||||
|
SDLAudioManager.initialize();
|
||||||
|
SDLControllerManager.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function stores the current activity (SDL or not)
|
||||||
|
public static void setContext(Context context) {
|
||||||
|
SDLAudioManager.setContext(context);
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||||
|
loadLibrary(libraryName, mContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadLibrary(String libraryName, Context context) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
|
||||||
|
|
||||||
|
if (libraryName == null) {
|
||||||
|
throw new NullPointerException("No library name provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Let's see if we have ReLinker available in the project. This is necessary for
|
||||||
|
// some projects that have huge numbers of local libraries bundled, and thus may
|
||||||
|
// trip a bug in Android's native library loader which ReLinker works around. (If
|
||||||
|
// loadLibrary works properly, ReLinker will simply use the normal Android method
|
||||||
|
// internally.)
|
||||||
|
//
|
||||||
|
// To use ReLinker, just add it as a dependency. For more information, see
|
||||||
|
// https://github.com/KeepSafe/ReLinker for ReLinker's repository.
|
||||||
|
//
|
||||||
|
Class<?> relinkClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
|
||||||
|
Class<?> relinkListenerClass = context.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
|
||||||
|
Class<?> contextClass = context.getClassLoader().loadClass("android.content.Context");
|
||||||
|
Class<?> stringClass = context.getClassLoader().loadClass("java.lang.String");
|
||||||
|
|
||||||
|
// Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
|
||||||
|
// they've changed during updates.
|
||||||
|
Method forceMethod = relinkClass.getDeclaredMethod("force");
|
||||||
|
Object relinkInstance = forceMethod.invoke(null);
|
||||||
|
Class<?> relinkInstanceClass = relinkInstance.getClass();
|
||||||
|
|
||||||
|
// Actually load the library!
|
||||||
|
Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
|
||||||
|
loadMethod.invoke(relinkInstance, context, libraryName, null, null);
|
||||||
|
}
|
||||||
|
catch (final Throwable e) {
|
||||||
|
// Fall back
|
||||||
|
try {
|
||||||
|
System.loadLibrary(libraryName);
|
||||||
|
}
|
||||||
|
catch (final UnsatisfiedLinkError ule) {
|
||||||
|
throw ule;
|
||||||
|
}
|
||||||
|
catch (final SecurityException se) {
|
||||||
|
throw se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Context mContext;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,514 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioDeviceCallback;
|
||||||
|
import android.media.AudioDeviceInfo;
|
||||||
|
import android.media.AudioFormat;
|
||||||
|
import android.media.AudioManager;
|
||||||
|
import android.media.AudioRecord;
|
||||||
|
import android.media.AudioTrack;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class SDLAudioManager {
|
||||||
|
protected static final String TAG = "SDLAudio";
|
||||||
|
|
||||||
|
protected static AudioTrack mAudioTrack;
|
||||||
|
protected static AudioRecord mAudioRecord;
|
||||||
|
protected static Context mContext;
|
||||||
|
|
||||||
|
private static final int[] NO_DEVICES = {};
|
||||||
|
|
||||||
|
private static AudioDeviceCallback mAudioDeviceCallback;
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
mAudioTrack = null;
|
||||||
|
mAudioRecord = null;
|
||||||
|
mAudioDeviceCallback = null;
|
||||||
|
|
||||||
|
if(Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */)
|
||||||
|
{
|
||||||
|
mAudioDeviceCallback = new AudioDeviceCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
|
||||||
|
Arrays.stream(addedDevices).forEach(deviceInfo -> addAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
|
||||||
|
Arrays.stream(removedDevices).forEach(deviceInfo -> removeAudioDevice(deviceInfo.isSink(), deviceInfo.getId()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setContext(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
if (context != null) {
|
||||||
|
registerAudioDeviceCallback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void release(Context context) {
|
||||||
|
unregisterAudioDeviceCallback(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
|
||||||
|
protected static String getAudioFormatString(int audioFormat) {
|
||||||
|
switch (audioFormat) {
|
||||||
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
|
return "8-bit";
|
||||||
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
|
return "16-bit";
|
||||||
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
return "float";
|
||||||
|
default:
|
||||||
|
return Integer.toString(audioFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int[] open(boolean isCapture, int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
int channelConfig;
|
||||||
|
int sampleSize;
|
||||||
|
int frameSize;
|
||||||
|
|
||||||
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", requested " + desiredFrames + " frames of " + desiredChannels + " channel " + getAudioFormatString(audioFormat) + " audio at " + sampleRate + " Hz");
|
||||||
|
|
||||||
|
/* On older devices let's use known good settings */
|
||||||
|
if (Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
if (desiredChannels > 2) {
|
||||||
|
desiredChannels = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* AudioTrack has sample rate limitation of 48000 (fixed in 5.0.2) */
|
||||||
|
if (Build.VERSION.SDK_INT < 22 /* Android 5.1 (LOLLIPOP_MR1) */) {
|
||||||
|
if (sampleRate < 8000) {
|
||||||
|
sampleRate = 8000;
|
||||||
|
} else if (sampleRate > 48000) {
|
||||||
|
sampleRate = 48000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
|
||||||
|
int minSDKVersion = (isCapture ? 23 /* Android 6.0 (M) */ : 21 /* Android 5.0 (LOLLIPOP) */);
|
||||||
|
if (Build.VERSION.SDK_INT < minSDKVersion) {
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (audioFormat)
|
||||||
|
{
|
||||||
|
case AudioFormat.ENCODING_PCM_8BIT:
|
||||||
|
sampleSize = 1;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_16BIT:
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
case AudioFormat.ENCODING_PCM_FLOAT:
|
||||||
|
sampleSize = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested format " + audioFormat + ", getting ENCODING_PCM_16BIT");
|
||||||
|
audioFormat = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
|
sampleSize = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCapture) {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (desiredChannels) {
|
||||||
|
case 1:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
if (Build.VERSION.SDK_INT >= 23 /* Android 6.0 (M) */) {
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||||
|
} else {
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting 5.1 surround");
|
||||||
|
desiredChannels = 6;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.v(TAG, "Requested " + desiredChannels + " channels, getting stereo");
|
||||||
|
desiredChannels = 2;
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Log.v(TAG, "Speaker configuration (and order of channels):");
|
||||||
|
|
||||||
|
if ((channelConfig & 0x00000004) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000008) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000010) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000020) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_LOW_FREQUENCY");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000040) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000080) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_RIGHT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000100) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_LEFT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000200) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_FRONT_RIGHT_OF_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000400) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_BACK_CENTER");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00000800) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_LEFT");
|
||||||
|
}
|
||||||
|
if ((channelConfig & 0x00001000) != 0) {
|
||||||
|
Log.v(TAG, " CHANNEL_OUT_SIDE_RIGHT");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
frameSize = (sampleSize * desiredChannels);
|
||||||
|
|
||||||
|
// Let the user pick a larger buffer if they really want -- but ye
|
||||||
|
// gods they probably shouldn't, the minimums are horrifyingly high
|
||||||
|
// latency already
|
||||||
|
int minBufferSize;
|
||||||
|
if (isCapture) {
|
||||||
|
minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
} else {
|
||||||
|
minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
|
||||||
|
}
|
||||||
|
desiredFrames = Math.max(desiredFrames, (minBufferSize + frameSize - 1) / frameSize);
|
||||||
|
|
||||||
|
int[] results = new int[4];
|
||||||
|
|
||||||
|
if (isCapture) {
|
||||||
|
if (mAudioRecord == null) {
|
||||||
|
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate,
|
||||||
|
channelConfig, audioFormat, desiredFrames * frameSize);
|
||||||
|
|
||||||
|
// see notes about AudioTrack state in audioOpen(), above. Probably also applies here.
|
||||||
|
if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
|
||||||
|
Log.e(TAG, "Failed during initialization of AudioRecord");
|
||||||
|
mAudioRecord.release();
|
||||||
|
mAudioRecord = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||||
|
mAudioRecord.setPreferredDevice(getOutputAudioDeviceInfo(deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioRecord.startRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
results[0] = mAudioRecord.getSampleRate();
|
||||||
|
results[1] = mAudioRecord.getAudioFormat();
|
||||||
|
results[2] = mAudioRecord.getChannelCount();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
|
||||||
|
|
||||||
|
// Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
|
||||||
|
// Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
|
||||||
|
// Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
|
||||||
|
if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
|
||||||
|
/* Try again, with safer values */
|
||||||
|
|
||||||
|
Log.e(TAG, "Failed during initialization of Audio Track");
|
||||||
|
mAudioTrack.release();
|
||||||
|
mAudioTrack = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */ && deviceId != 0) {
|
||||||
|
mAudioTrack.setPreferredDevice(getInputAudioDeviceInfo(deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioTrack.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
results[0] = mAudioTrack.getSampleRate();
|
||||||
|
results[1] = mAudioTrack.getAudioFormat();
|
||||||
|
results[2] = mAudioTrack.getChannelCount();
|
||||||
|
}
|
||||||
|
results[3] = desiredFrames;
|
||||||
|
|
||||||
|
Log.v(TAG, "Opening " + (isCapture ? "capture" : "playback") + ", got " + results[3] + " frames of " + results[2] + " channel " + getAudioFormatString(results[1]) + " audio at " + results[0] + " Hz");
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AudioDeviceInfo getInputAudioDeviceInfo(int deviceId) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
|
||||||
|
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AudioDeviceInfo getOutputAudioDeviceInfo(int deviceId) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
|
||||||
|
.filter(deviceInfo -> deviceInfo.getId() == deviceId)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerAudioDeviceCallback() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audioManager.registerAudioDeviceCallback(mAudioDeviceCallback, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void unregisterAudioDeviceCallback(Context context) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
audioManager.unregisterAudioDeviceCallback(mAudioDeviceCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] getAudioOutputDevices() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||||
|
} else {
|
||||||
|
return NO_DEVICES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] getAudioInputDevices() {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
return Arrays.stream(audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).mapToInt(AudioDeviceInfo::getId).toArray();
|
||||||
|
} else {
|
||||||
|
return NO_DEVICES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] audioOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
return open(false, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteFloatBuffer(float[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < 21 /* Android 5.0 (LOLLIPOP) */) {
|
||||||
|
Log.e(TAG, "Attempted to make an incompatible audio call with uninitialized audio! (floating-point output is supported since Android 5.0 Lollipop)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length;) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i, AudioTrack.WRITE_BLOCKING);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(float)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteShortBuffer(short[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length;) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(short)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void audioWriteByteBuffer(byte[] buffer) {
|
||||||
|
if (mAudioTrack == null) {
|
||||||
|
Log.e(TAG, "Attempted to make audio call with uninitialized audio!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.length; ) {
|
||||||
|
int result = mAudioTrack.write(buffer, i, buffer.length - i);
|
||||||
|
if (result > 0) {
|
||||||
|
i += result;
|
||||||
|
} else if (result == 0) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1);
|
||||||
|
} catch(InterruptedException e) {
|
||||||
|
// Nom nom
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "SDL audio: error return from write(byte)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static int[] captureOpen(int sampleRate, int audioFormat, int desiredChannels, int desiredFrames, int deviceId) {
|
||||||
|
return open(true, sampleRate, audioFormat, desiredChannels, desiredFrames, deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadFloatBuffer(float[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadShortBuffer(short[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static int captureReadByteBuffer(byte[] buffer, boolean blocking) {
|
||||||
|
if (Build.VERSION.SDK_INT < 23 /* Android 6.0 (M) */) {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length);
|
||||||
|
} else {
|
||||||
|
return mAudioRecord.read(buffer, 0, buffer.length, blocking ? AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void audioClose() {
|
||||||
|
if (mAudioTrack != null) {
|
||||||
|
mAudioTrack.stop();
|
||||||
|
mAudioTrack.release();
|
||||||
|
mAudioTrack = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void captureClose() {
|
||||||
|
if (mAudioRecord != null) {
|
||||||
|
mAudioRecord.stop();
|
||||||
|
mAudioRecord.release();
|
||||||
|
mAudioRecord = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** This method is called by SDL using JNI. */
|
||||||
|
public static void audioSetThreadPriority(boolean iscapture, int device_id) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
/* Set thread name */
|
||||||
|
if (iscapture) {
|
||||||
|
Thread.currentThread().setName("SDLAudioC" + device_id);
|
||||||
|
} else {
|
||||||
|
Thread.currentThread().setName("SDLAudioP" + device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set thread priority */
|
||||||
|
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.v(TAG, "modify thread properties failed " + e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static native int nativeSetupJNI();
|
||||||
|
|
||||||
|
public static native void removeAudioDevice(boolean isCapture, int deviceId);
|
||||||
|
|
||||||
|
public static native void addAudioDevice(boolean isCapture, int deviceId);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,856 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.VibrationEffect;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
|
||||||
|
public class SDLControllerManager
|
||||||
|
{
|
||||||
|
|
||||||
|
public static native int nativeSetupJNI();
|
||||||
|
|
||||||
|
public static native int nativeAddJoystick(int device_id, String name, String desc,
|
||||||
|
int vendor_id, int product_id,
|
||||||
|
boolean is_accelerometer, int button_mask,
|
||||||
|
int naxes, int axis_mask, int nhats, int nballs);
|
||||||
|
public static native int nativeRemoveJoystick(int device_id);
|
||||||
|
public static native int nativeAddHaptic(int device_id, String name);
|
||||||
|
public static native int nativeRemoveHaptic(int device_id);
|
||||||
|
public static native int onNativePadDown(int device_id, int keycode);
|
||||||
|
public static native int onNativePadUp(int device_id, int keycode);
|
||||||
|
public static native void onNativeJoy(int device_id, int axis,
|
||||||
|
float value);
|
||||||
|
public static native void onNativeHat(int device_id, int hat_id,
|
||||||
|
int x, int y);
|
||||||
|
|
||||||
|
protected static SDLJoystickHandler mJoystickHandler;
|
||||||
|
protected static SDLHapticHandler mHapticHandler;
|
||||||
|
|
||||||
|
private static final String TAG = "SDLControllerManager";
|
||||||
|
|
||||||
|
public static void initialize() {
|
||||||
|
if (mJoystickHandler == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 19 /* Android 4.4 (KITKAT) */) {
|
||||||
|
mJoystickHandler = new SDLJoystickHandler_API19();
|
||||||
|
} else {
|
||||||
|
mJoystickHandler = new SDLJoystickHandler_API16();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mHapticHandler == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26 /* Android 8.0 (O) */) {
|
||||||
|
mHapticHandler = new SDLHapticHandler_API26();
|
||||||
|
} else {
|
||||||
|
mHapticHandler = new SDLHapticHandler();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
|
||||||
|
public static boolean handleJoystickMotionEvent(MotionEvent event) {
|
||||||
|
return mJoystickHandler.handleMotionEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void pollInputDevices() {
|
||||||
|
mJoystickHandler.pollInputDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void pollHapticDevices() {
|
||||||
|
mHapticHandler.pollHapticDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void hapticRun(int device_id, float intensity, int length) {
|
||||||
|
mHapticHandler.run(device_id, intensity, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called by SDL using JNI.
|
||||||
|
*/
|
||||||
|
public static void hapticStop(int device_id)
|
||||||
|
{
|
||||||
|
mHapticHandler.stop(device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a given device is considered a possible SDL joystick
|
||||||
|
public static boolean isDeviceSDLJoystick(int deviceId) {
|
||||||
|
InputDevice device = InputDevice.getDevice(deviceId);
|
||||||
|
// We cannot use InputDevice.isVirtual before API 16, so let's accept
|
||||||
|
// only nonnegative device ids (VIRTUAL_KEYBOARD equals -1)
|
||||||
|
if ((device == null) || (deviceId < 0)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int sources = device.getSources();
|
||||||
|
|
||||||
|
/* This is called for every button press, so let's not spam the logs */
|
||||||
|
/*
|
||||||
|
if ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " has class joystick.");
|
||||||
|
}
|
||||||
|
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " is a dpad.");
|
||||||
|
}
|
||||||
|
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||||
|
Log.v(TAG, "Input device " + device.getName() + " is a gamepad.");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return ((sources & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ||
|
||||||
|
((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) ||
|
||||||
|
((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLJoystickHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles given MotionEvent.
|
||||||
|
* @param event the event to be handled.
|
||||||
|
* @return if given event was processed.
|
||||||
|
*/
|
||||||
|
public boolean handleMotionEvent(MotionEvent event) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles adding and removing of input devices.
|
||||||
|
*/
|
||||||
|
public void pollInputDevices() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual joystick functionality available for API >= 12 devices */
|
||||||
|
class SDLJoystickHandler_API16 extends SDLJoystickHandler {
|
||||||
|
|
||||||
|
static class SDLJoystick {
|
||||||
|
public int device_id;
|
||||||
|
public String name;
|
||||||
|
public String desc;
|
||||||
|
public ArrayList<InputDevice.MotionRange> axes;
|
||||||
|
public ArrayList<InputDevice.MotionRange> hats;
|
||||||
|
}
|
||||||
|
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
|
||||||
|
@Override
|
||||||
|
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
|
||||||
|
// Some controllers, like the Moga Pro 2, return AXIS_GAS (22) for right trigger and AXIS_BRAKE (23) for left trigger - swap them so they're sorted in the right order for SDL
|
||||||
|
int arg0Axis = arg0.getAxis();
|
||||||
|
int arg1Axis = arg1.getAxis();
|
||||||
|
if (arg0Axis == MotionEvent.AXIS_GAS) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_BRAKE;
|
||||||
|
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_GAS;
|
||||||
|
}
|
||||||
|
if (arg1Axis == MotionEvent.AXIS_GAS) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_BRAKE;
|
||||||
|
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_GAS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the AXIS_Z is sorted between AXIS_RY and AXIS_RZ.
|
||||||
|
// This is because the usual pairing are:
|
||||||
|
// - AXIS_X + AXIS_Y (left stick).
|
||||||
|
// - AXIS_RX, AXIS_RY (sometimes the right stick, sometimes triggers).
|
||||||
|
// - AXIS_Z, AXIS_RZ (sometimes the right stick, sometimes triggers).
|
||||||
|
// This sorts the axes in the above order, which tends to be correct
|
||||||
|
// for Xbox-ish game pads that have the right stick on RX/RY and the
|
||||||
|
// triggers on Z/RZ.
|
||||||
|
//
|
||||||
|
// Gamepads that don't have AXIS_Z/AXIS_RZ but use
|
||||||
|
// AXIS_LTRIGGER/AXIS_RTRIGGER are unaffected by this.
|
||||||
|
//
|
||||||
|
// References:
|
||||||
|
// - https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
|
||||||
|
// - https://www.kernel.org/doc/html/latest/input/gamepad.html
|
||||||
|
if (arg0Axis == MotionEvent.AXIS_Z) {
|
||||||
|
arg0Axis = MotionEvent.AXIS_RZ - 1;
|
||||||
|
} else if (arg0Axis > MotionEvent.AXIS_Z && arg0Axis < MotionEvent.AXIS_RZ) {
|
||||||
|
--arg0Axis;
|
||||||
|
}
|
||||||
|
if (arg1Axis == MotionEvent.AXIS_Z) {
|
||||||
|
arg1Axis = MotionEvent.AXIS_RZ - 1;
|
||||||
|
} else if (arg1Axis > MotionEvent.AXIS_Z && arg1Axis < MotionEvent.AXIS_RZ) {
|
||||||
|
--arg1Axis;
|
||||||
|
}
|
||||||
|
|
||||||
|
return arg0Axis - arg1Axis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<SDLJoystick> mJoysticks;
|
||||||
|
|
||||||
|
public SDLJoystickHandler_API16() {
|
||||||
|
|
||||||
|
mJoysticks = new ArrayList<SDLJoystick>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pollInputDevices() {
|
||||||
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
|
|
||||||
|
for (int device_id : deviceIds) {
|
||||||
|
if (SDLControllerManager.isDeviceSDLJoystick(device_id)) {
|
||||||
|
SDLJoystick joystick = getJoystick(device_id);
|
||||||
|
if (joystick == null) {
|
||||||
|
InputDevice joystickDevice = InputDevice.getDevice(device_id);
|
||||||
|
joystick = new SDLJoystick();
|
||||||
|
joystick.device_id = device_id;
|
||||||
|
joystick.name = joystickDevice.getName();
|
||||||
|
joystick.desc = getJoystickDescriptor(joystickDevice);
|
||||||
|
joystick.axes = new ArrayList<InputDevice.MotionRange>();
|
||||||
|
joystick.hats = new ArrayList<InputDevice.MotionRange>();
|
||||||
|
|
||||||
|
List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
|
||||||
|
Collections.sort(ranges, new RangeComparator());
|
||||||
|
for (InputDevice.MotionRange range : ranges) {
|
||||||
|
if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
|
||||||
|
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
|
||||||
|
joystick.hats.add(range);
|
||||||
|
} else {
|
||||||
|
joystick.axes.add(range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mJoysticks.add(joystick);
|
||||||
|
SDLControllerManager.nativeAddJoystick(joystick.device_id, joystick.name, joystick.desc,
|
||||||
|
getVendorId(joystickDevice), getProductId(joystickDevice), false,
|
||||||
|
getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check removed devices */
|
||||||
|
ArrayList<Integer> removedDevices = null;
|
||||||
|
for (SDLJoystick joystick : mJoysticks) {
|
||||||
|
int device_id = joystick.device_id;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < deviceIds.length; i++) {
|
||||||
|
if (device_id == deviceIds[i]) break;
|
||||||
|
}
|
||||||
|
if (i == deviceIds.length) {
|
||||||
|
if (removedDevices == null) {
|
||||||
|
removedDevices = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
removedDevices.add(device_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedDevices != null) {
|
||||||
|
for (int device_id : removedDevices) {
|
||||||
|
SDLControllerManager.nativeRemoveJoystick(device_id);
|
||||||
|
for (int i = 0; i < mJoysticks.size(); i++) {
|
||||||
|
if (mJoysticks.get(i).device_id == device_id) {
|
||||||
|
mJoysticks.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SDLJoystick getJoystick(int device_id) {
|
||||||
|
for (SDLJoystick joystick : mJoysticks) {
|
||||||
|
if (joystick.device_id == device_id) {
|
||||||
|
return joystick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMotionEvent(MotionEvent event) {
|
||||||
|
int actionPointerIndex = event.getActionIndex();
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
if (action == MotionEvent.ACTION_MOVE) {
|
||||||
|
SDLJoystick joystick = getJoystick(event.getDeviceId());
|
||||||
|
if (joystick != null) {
|
||||||
|
for (int i = 0; i < joystick.axes.size(); i++) {
|
||||||
|
InputDevice.MotionRange range = joystick.axes.get(i);
|
||||||
|
/* Normalize the value to -1...1 */
|
||||||
|
float value = (event.getAxisValue(range.getAxis(), actionPointerIndex) - range.getMin()) / range.getRange() * 2.0f - 1.0f;
|
||||||
|
SDLControllerManager.onNativeJoy(joystick.device_id, i, value);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < joystick.hats.size() / 2; i++) {
|
||||||
|
int hatX = Math.round(event.getAxisValue(joystick.hats.get(2 * i).getAxis(), actionPointerIndex));
|
||||||
|
int hatY = Math.round(event.getAxisValue(joystick.hats.get(2 * i + 1).getAxis(), actionPointerIndex));
|
||||||
|
SDLControllerManager.onNativeHat(joystick.device_id, i, hatX, hatY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJoystickDescriptor(InputDevice joystickDevice) {
|
||||||
|
String desc = joystickDevice.getDescriptor();
|
||||||
|
|
||||||
|
if (desc != null && !desc.isEmpty()) {
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return joystickDevice.getName();
|
||||||
|
}
|
||||||
|
public int getProductId(InputDevice joystickDevice) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int getVendorId(InputDevice joystickDevice) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public int getButtonMask(InputDevice joystickDevice) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLJoystickHandler_API19 extends SDLJoystickHandler_API16 {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getProductId(InputDevice joystickDevice) {
|
||||||
|
return joystickDevice.getProductId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVendorId(InputDevice joystickDevice) {
|
||||||
|
return joystickDevice.getVendorId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAxisMask(List<InputDevice.MotionRange> ranges) {
|
||||||
|
// For compatibility, keep computing the axis mask like before,
|
||||||
|
// only really distinguishing 2, 4 and 6 axes.
|
||||||
|
int axis_mask = 0;
|
||||||
|
if (ranges.size() >= 2) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_LEFTX) | (1 << SDL_GAMEPAD_AXIS_LEFTY))
|
||||||
|
axis_mask |= 0x0003;
|
||||||
|
}
|
||||||
|
if (ranges.size() >= 4) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_RIGHTX) | (1 << SDL_GAMEPAD_AXIS_RIGHTY))
|
||||||
|
axis_mask |= 0x000c;
|
||||||
|
}
|
||||||
|
if (ranges.size() >= 6) {
|
||||||
|
// ((1 << SDL_GAMEPAD_AXIS_LEFT_TRIGGER) | (1 << SDL_GAMEPAD_AXIS_RIGHT_TRIGGER))
|
||||||
|
axis_mask |= 0x0030;
|
||||||
|
}
|
||||||
|
// Also add an indicator bit for whether the sorting order has changed.
|
||||||
|
// This serves to disable outdated gamecontrollerdb.txt mappings.
|
||||||
|
boolean have_z = false;
|
||||||
|
boolean have_past_z_before_rz = false;
|
||||||
|
for (InputDevice.MotionRange range : ranges) {
|
||||||
|
int axis = range.getAxis();
|
||||||
|
if (axis == MotionEvent.AXIS_Z) {
|
||||||
|
have_z = true;
|
||||||
|
} else if (axis > MotionEvent.AXIS_Z && axis < MotionEvent.AXIS_RZ) {
|
||||||
|
have_past_z_before_rz = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (have_z && have_past_z_before_rz) {
|
||||||
|
// If both these exist, the compare() function changed sorting order.
|
||||||
|
// Set a bit to indicate this fact.
|
||||||
|
axis_mask |= 0x8000;
|
||||||
|
}
|
||||||
|
return axis_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getButtonMask(InputDevice joystickDevice) {
|
||||||
|
int button_mask = 0;
|
||||||
|
int[] keys = new int[] {
|
||||||
|
KeyEvent.KEYCODE_BUTTON_A,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_B,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_X,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Y,
|
||||||
|
KeyEvent.KEYCODE_BACK,
|
||||||
|
KeyEvent.KEYCODE_MENU,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_MODE,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_START,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBL,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_THUMBR,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L1,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R1,
|
||||||
|
KeyEvent.KEYCODE_DPAD_UP,
|
||||||
|
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||||
|
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_SELECT,
|
||||||
|
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||||
|
|
||||||
|
// These don't map into any SDL controller buttons directly
|
||||||
|
KeyEvent.KEYCODE_BUTTON_L2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_R2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_C,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_Z,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_1,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_2,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_3,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_4,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_5,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_6,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_7,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_8,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_9,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_10,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_11,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_12,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_13,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_14,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_15,
|
||||||
|
KeyEvent.KEYCODE_BUTTON_16,
|
||||||
|
};
|
||||||
|
int[] masks = new int[] {
|
||||||
|
(1 << 0), // A -> A
|
||||||
|
(1 << 1), // B -> B
|
||||||
|
(1 << 2), // X -> X
|
||||||
|
(1 << 3), // Y -> Y
|
||||||
|
(1 << 4), // BACK -> BACK
|
||||||
|
(1 << 6), // MENU -> START
|
||||||
|
(1 << 5), // MODE -> GUIDE
|
||||||
|
(1 << 6), // START -> START
|
||||||
|
(1 << 7), // THUMBL -> LEFTSTICK
|
||||||
|
(1 << 8), // THUMBR -> RIGHTSTICK
|
||||||
|
(1 << 9), // L1 -> LEFTSHOULDER
|
||||||
|
(1 << 10), // R1 -> RIGHTSHOULDER
|
||||||
|
(1 << 11), // DPAD_UP -> DPAD_UP
|
||||||
|
(1 << 12), // DPAD_DOWN -> DPAD_DOWN
|
||||||
|
(1 << 13), // DPAD_LEFT -> DPAD_LEFT
|
||||||
|
(1 << 14), // DPAD_RIGHT -> DPAD_RIGHT
|
||||||
|
(1 << 4), // SELECT -> BACK
|
||||||
|
(1 << 0), // DPAD_CENTER -> A
|
||||||
|
(1 << 15), // L2 -> ??
|
||||||
|
(1 << 16), // R2 -> ??
|
||||||
|
(1 << 17), // C -> ??
|
||||||
|
(1 << 18), // Z -> ??
|
||||||
|
(1 << 20), // 1 -> ??
|
||||||
|
(1 << 21), // 2 -> ??
|
||||||
|
(1 << 22), // 3 -> ??
|
||||||
|
(1 << 23), // 4 -> ??
|
||||||
|
(1 << 24), // 5 -> ??
|
||||||
|
(1 << 25), // 6 -> ??
|
||||||
|
(1 << 26), // 7 -> ??
|
||||||
|
(1 << 27), // 8 -> ??
|
||||||
|
(1 << 28), // 9 -> ??
|
||||||
|
(1 << 29), // 10 -> ??
|
||||||
|
(1 << 30), // 11 -> ??
|
||||||
|
(1 << 31), // 12 -> ??
|
||||||
|
// We're out of room...
|
||||||
|
0xFFFFFFFF, // 13 -> ??
|
||||||
|
0xFFFFFFFF, // 14 -> ??
|
||||||
|
0xFFFFFFFF, // 15 -> ??
|
||||||
|
0xFFFFFFFF, // 16 -> ??
|
||||||
|
};
|
||||||
|
boolean[] has_keys = joystickDevice.hasKeys(keys);
|
||||||
|
for (int i = 0; i < keys.length; ++i) {
|
||||||
|
if (has_keys[i]) {
|
||||||
|
button_mask |= masks[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return button_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLHapticHandler_API26 extends SDLHapticHandler {
|
||||||
|
@Override
|
||||||
|
public void run(int device_id, float intensity, int length) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
Log.d("SDL", "Rtest: Vibe with intensity " + intensity + " for " + length);
|
||||||
|
if (intensity == 0.0f) {
|
||||||
|
stop(device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int vibeValue = Math.round(intensity * 255);
|
||||||
|
|
||||||
|
if (vibeValue > 255) {
|
||||||
|
vibeValue = 255;
|
||||||
|
}
|
||||||
|
if (vibeValue < 1) {
|
||||||
|
stop(device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
haptic.vib.vibrate(VibrationEffect.createOneShot(length, vibeValue));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// Fall back to the generic method, which uses DEFAULT_AMPLITUDE, but works even if
|
||||||
|
// something went horribly wrong with the Android 8.0 APIs.
|
||||||
|
haptic.vib.vibrate(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLHapticHandler {
|
||||||
|
|
||||||
|
static class SDLHaptic {
|
||||||
|
public int device_id;
|
||||||
|
public String name;
|
||||||
|
public Vibrator vib;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ArrayList<SDLHaptic> mHaptics;
|
||||||
|
|
||||||
|
public SDLHapticHandler() {
|
||||||
|
mHaptics = new ArrayList<SDLHaptic>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(int device_id, float intensity, int length) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
haptic.vib.vibrate(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop(int device_id) {
|
||||||
|
SDLHaptic haptic = getHaptic(device_id);
|
||||||
|
if (haptic != null) {
|
||||||
|
haptic.vib.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pollHapticDevices() {
|
||||||
|
|
||||||
|
final int deviceId_VIBRATOR_SERVICE = 999999;
|
||||||
|
boolean hasVibratorService = false;
|
||||||
|
|
||||||
|
int[] deviceIds = InputDevice.getDeviceIds();
|
||||||
|
// It helps processing the device ids in reverse order
|
||||||
|
// For example, in the case of the XBox 360 wireless dongle,
|
||||||
|
// so the first controller seen by SDL matches what the receiver
|
||||||
|
// considers to be the first controller
|
||||||
|
|
||||||
|
for (int i = deviceIds.length - 1; i > -1; i--) {
|
||||||
|
SDLHaptic haptic = getHaptic(deviceIds[i]);
|
||||||
|
if (haptic == null) {
|
||||||
|
InputDevice device = InputDevice.getDevice(deviceIds[i]);
|
||||||
|
Vibrator vib = device.getVibrator();
|
||||||
|
if (vib != null) {
|
||||||
|
if (vib.hasVibrator()) {
|
||||||
|
haptic = new SDLHaptic();
|
||||||
|
haptic.device_id = deviceIds[i];
|
||||||
|
haptic.name = device.getName();
|
||||||
|
haptic.vib = vib;
|
||||||
|
mHaptics.add(haptic);
|
||||||
|
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check VIBRATOR_SERVICE */
|
||||||
|
Vibrator vib = (Vibrator) SDL.getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
if (vib != null) {
|
||||||
|
hasVibratorService = vib.hasVibrator();
|
||||||
|
|
||||||
|
if (hasVibratorService) {
|
||||||
|
SDLHaptic haptic = getHaptic(deviceId_VIBRATOR_SERVICE);
|
||||||
|
if (haptic == null) {
|
||||||
|
haptic = new SDLHaptic();
|
||||||
|
haptic.device_id = deviceId_VIBRATOR_SERVICE;
|
||||||
|
haptic.name = "VIBRATOR_SERVICE";
|
||||||
|
haptic.vib = vib;
|
||||||
|
mHaptics.add(haptic);
|
||||||
|
SDLControllerManager.nativeAddHaptic(haptic.device_id, haptic.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check removed devices */
|
||||||
|
ArrayList<Integer> removedDevices = null;
|
||||||
|
for (SDLHaptic haptic : mHaptics) {
|
||||||
|
int device_id = haptic.device_id;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < deviceIds.length; i++) {
|
||||||
|
if (device_id == deviceIds[i]) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_id != deviceId_VIBRATOR_SERVICE || !hasVibratorService) {
|
||||||
|
if (i == deviceIds.length) {
|
||||||
|
if (removedDevices == null) {
|
||||||
|
removedDevices = new ArrayList<Integer>();
|
||||||
|
}
|
||||||
|
removedDevices.add(device_id);
|
||||||
|
}
|
||||||
|
} // else: don't remove the vibrator if it is still present
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removedDevices != null) {
|
||||||
|
for (int device_id : removedDevices) {
|
||||||
|
SDLControllerManager.nativeRemoveHaptic(device_id);
|
||||||
|
for (int i = 0; i < mHaptics.size(); i++) {
|
||||||
|
if (mHaptics.get(i).device_id == device_id) {
|
||||||
|
mHaptics.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SDLHaptic getHaptic(int device_id) {
|
||||||
|
for (SDLHaptic haptic : mHaptics) {
|
||||||
|
if (haptic.device_id == device_id) {
|
||||||
|
return haptic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
float x, y;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
switch ( event.getSource() ) {
|
||||||
|
case InputDevice.SOURCE_JOYSTICK:
|
||||||
|
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reclaimRelativeMouseModeIfNeeded()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API24 extends SDLGenericMotionListener_API12 {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
|
||||||
|
private boolean mRelativeModeEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
|
||||||
|
// Handle relative mouse mode
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE) {
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
if (action == MotionEvent.ACTION_HOVER_MOVE) {
|
||||||
|
float x = event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||||
|
float y = event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed, call SDLGenericMotionListener_API12 method
|
||||||
|
return super.onGenericMotion(v, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return mRelativeModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
mRelativeModeEnabled = enabled;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
||||||
|
} else {
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
if (mRelativeModeEnabled) {
|
||||||
|
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
||||||
|
} else {
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SDLGenericMotionListener_API26 extends SDLGenericMotionListener_API24 {
|
||||||
|
// Generic Motion (mouse hover, joystick...) events go here
|
||||||
|
private boolean mRelativeModeEnabled;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||||
|
float x, y;
|
||||||
|
int action;
|
||||||
|
|
||||||
|
switch ( event.getSource() ) {
|
||||||
|
case InputDevice.SOURCE_JOYSTICK:
|
||||||
|
return SDLControllerManager.handleJoystickMotionEvent(event);
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE:
|
||||||
|
// DeX desktop mouse cursor is a separate non-standard input type.
|
||||||
|
case InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case InputDevice.SOURCE_MOUSE_RELATIVE:
|
||||||
|
action = event.getActionMasked();
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event was not managed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsRelativeMouse() {
|
||||||
|
return (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean inRelativeMode() {
|
||||||
|
return mRelativeModeEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setRelativeMouseEnabled(boolean enabled) {
|
||||||
|
if (!SDLActivity.isDeXMode() || Build.VERSION.SDK_INT >= 27 /* Android 8.1 (O_MR1) */) {
|
||||||
|
if (enabled) {
|
||||||
|
SDLActivity.getContentView().requestPointerCapture();
|
||||||
|
} else {
|
||||||
|
SDLActivity.getContentView().releasePointerCapture();
|
||||||
|
}
|
||||||
|
mRelativeModeEnabled = enabled;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reclaimRelativeMouseModeIfNeeded()
|
||||||
|
{
|
||||||
|
if (mRelativeModeEnabled && !SDLActivity.isDeXMode()) {
|
||||||
|
SDLActivity.getContentView().requestPointerCapture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventX(MotionEvent event) {
|
||||||
|
// Relative mouse in capture mode will only have relative for X/Y
|
||||||
|
return event.getX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getEventY(MotionEvent event) {
|
||||||
|
// Relative mouse in capture mode will only have relative for X/Y
|
||||||
|
return event.getY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
package org.libsdl.app;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
SDLSurface. This is what we draw on, so we need to know when it's created
|
||||||
|
in order to do anything useful.
|
||||||
|
|
||||||
|
Because of this, that's where we set up the SDL thread
|
||||||
|
*/
|
||||||
|
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
|
||||||
|
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
|
||||||
|
|
||||||
|
// Sensors
|
||||||
|
protected SensorManager mSensorManager;
|
||||||
|
protected Display mDisplay;
|
||||||
|
|
||||||
|
// Keep track of the surface size to normalize touch events
|
||||||
|
protected float mWidth, mHeight;
|
||||||
|
|
||||||
|
// Is SurfaceView ready for rendering
|
||||||
|
public boolean mIsSurfaceReady;
|
||||||
|
|
||||||
|
// Startup
|
||||||
|
public SDLSurface(Context context) {
|
||||||
|
super(context);
|
||||||
|
getHolder().addCallback(this);
|
||||||
|
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
requestFocus();
|
||||||
|
setOnKeyListener(this);
|
||||||
|
setOnTouchListener(this);
|
||||||
|
|
||||||
|
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
|
||||||
|
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
|
||||||
|
|
||||||
|
setOnGenericMotionListener(SDLActivity.getMotionListener());
|
||||||
|
|
||||||
|
// Some arbitrary defaults to avoid a potential division by zero
|
||||||
|
mWidth = 1.0f;
|
||||||
|
mHeight = 1.0f;
|
||||||
|
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handlePause() {
|
||||||
|
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleResume() {
|
||||||
|
setFocusable(true);
|
||||||
|
setFocusableInTouchMode(true);
|
||||||
|
requestFocus();
|
||||||
|
setOnKeyListener(this);
|
||||||
|
setOnTouchListener(this);
|
||||||
|
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Surface getNativeSurface() {
|
||||||
|
return getHolder().getSurface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when we have a valid drawing surface
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
Log.v("SDL", "surfaceCreated()");
|
||||||
|
SDLActivity.onNativeSurfaceCreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when we lose the surface
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
Log.v("SDL", "surfaceDestroyed()");
|
||||||
|
|
||||||
|
// Transition to pause, if needed
|
||||||
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
|
||||||
|
SDLActivity.handleNativeState();
|
||||||
|
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
SDLActivity.onNativeSurfaceDestroyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the surface is resized
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder,
|
||||||
|
int format, int width, int height) {
|
||||||
|
Log.v("SDL", "surfaceChanged()");
|
||||||
|
|
||||||
|
if (SDLActivity.mSingleton == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
int nDeviceWidth = width;
|
||||||
|
int nDeviceHeight = height;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= 17 /* Android 4.2 (JELLY_BEAN_MR1) */) {
|
||||||
|
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||||
|
mDisplay.getRealMetrics( realMetrics );
|
||||||
|
nDeviceWidth = realMetrics.widthPixels;
|
||||||
|
nDeviceHeight = realMetrics.heightPixels;
|
||||||
|
}
|
||||||
|
} catch(Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(SDLActivity.getContext()) {
|
||||||
|
// In case we're waiting on a size change after going fullscreen, send a notification.
|
||||||
|
SDLActivity.getContext().notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.v("SDL", "Window size: " + width + "x" + height);
|
||||||
|
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
|
||||||
|
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
|
||||||
|
SDLActivity.onNativeResize();
|
||||||
|
|
||||||
|
// Prevent a screen distortion glitch,
|
||||||
|
// for instance when the device is in Landscape and a Portrait App is resumed.
|
||||||
|
boolean skip = false;
|
||||||
|
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
|
||||||
|
|
||||||
|
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
|
||||||
|
if (mWidth > mHeight) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
|
||||||
|
if (mWidth < mHeight) {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special Patch for Square Resolution: Black Berry Passport
|
||||||
|
if (skip) {
|
||||||
|
double min = Math.min(mWidth, mHeight);
|
||||||
|
double max = Math.max(mWidth, mHeight);
|
||||||
|
|
||||||
|
if (max / min < 1.20) {
|
||||||
|
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't skip in MultiWindow.
|
||||||
|
if (skip) {
|
||||||
|
if (Build.VERSION.SDK_INT >= 24 /* Android 7.0 (N) */) {
|
||||||
|
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
|
||||||
|
Log.v("SDL", "Don't skip in Multi-Window");
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip) {
|
||||||
|
Log.v("SDL", "Skip .. Surface is not ready.");
|
||||||
|
mIsSurfaceReady = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
|
||||||
|
SDLActivity.onNativeSurfaceChanged();
|
||||||
|
|
||||||
|
/* Surface is ready */
|
||||||
|
mIsSurfaceReady = true;
|
||||||
|
|
||||||
|
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
|
||||||
|
SDLActivity.handleNativeState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key events
|
||||||
|
@Override
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Touch events
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent event) {
|
||||||
|
/* Ref: http://developer.android.com/training/gestures/multi.html */
|
||||||
|
int touchDevId = event.getDeviceId();
|
||||||
|
final int pointerCount = event.getPointerCount();
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
int pointerFingerId;
|
||||||
|
int i = -1;
|
||||||
|
float x,y,p;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prevent id to be -1, since it's used in SDL internal for synthetic events
|
||||||
|
* Appears when using Android emulator, eg:
|
||||||
|
* adb shell input mouse tap 100 100
|
||||||
|
* adb shell input touchscreen tap 100 100
|
||||||
|
*/
|
||||||
|
if (touchDevId < 0) {
|
||||||
|
touchDevId -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12290 = Samsung DeX mode desktop mouse
|
||||||
|
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
|
||||||
|
// 0x2 = SOURCE_CLASS_POINTER
|
||||||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
|
||||||
|
int mouseButton = 1;
|
||||||
|
try {
|
||||||
|
Object object = event.getClass().getMethod("getButtonState").invoke(event);
|
||||||
|
if (object != null) {
|
||||||
|
mouseButton = (Integer) object;
|
||||||
|
}
|
||||||
|
} catch(Exception ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
|
||||||
|
// if we are. We'll leverage our existing mouse motion listener
|
||||||
|
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
|
||||||
|
x = motionListener.getEventX(event);
|
||||||
|
y = motionListener.getEventY(event);
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
|
||||||
|
} else {
|
||||||
|
switch(action) {
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
for (i = 0; i < pointerCount; i++) {
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
// Primary pointer up/down, the index is always zero
|
||||||
|
i = 0;
|
||||||
|
/* fallthrough */
|
||||||
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
|
case MotionEvent.ACTION_POINTER_DOWN:
|
||||||
|
// Non primary pointer up/down
|
||||||
|
if (i == -1) {
|
||||||
|
i = event.getActionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_CANCEL:
|
||||||
|
for (i = 0; i < pointerCount; i++) {
|
||||||
|
pointerFingerId = event.getPointerId(i);
|
||||||
|
x = event.getX(i) / mWidth;
|
||||||
|
y = event.getY(i) / mHeight;
|
||||||
|
p = event.getPressure(i);
|
||||||
|
if (p > 1.0f) {
|
||||||
|
// may be larger than 1.0f on some devices
|
||||||
|
// see the documentation of getPressure(i)
|
||||||
|
p = 1.0f;
|
||||||
|
}
|
||||||
|
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sensor events
|
||||||
|
public void enableSensor(int sensortype, boolean enabled) {
|
||||||
|
// TODO: This uses getDefaultSensor - what if we have >1 accels?
|
||||||
|
if (enabled) {
|
||||||
|
mSensorManager.registerListener(this,
|
||||||
|
mSensorManager.getDefaultSensor(sensortype),
|
||||||
|
SensorManager.SENSOR_DELAY_GAME, null);
|
||||||
|
} else {
|
||||||
|
mSensorManager.unregisterListener(this,
|
||||||
|
mSensorManager.getDefaultSensor(sensortype));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||||
|
|
||||||
|
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
|
||||||
|
// We thus should check here.
|
||||||
|
int newOrientation;
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
switch (mDisplay.getRotation()) {
|
||||||
|
case Surface.ROTATION_90:
|
||||||
|
x = -event.values[1];
|
||||||
|
y = event.values[0];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_270:
|
||||||
|
x = event.values[1];
|
||||||
|
y = -event.values[0];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_180:
|
||||||
|
x = -event.values[0];
|
||||||
|
y = -event.values[1];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_0:
|
||||||
|
default:
|
||||||
|
x = event.values[0];
|
||||||
|
y = event.values[1];
|
||||||
|
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newOrientation != SDLActivity.mCurrentOrientation) {
|
||||||
|
SDLActivity.mCurrentOrientation = newOrientation;
|
||||||
|
SDLActivity.onNativeOrientationChanged(newOrientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
|
||||||
|
y / SensorManager.GRAVITY_EARTH,
|
||||||
|
event.values[2] / SensorManager.GRAVITY_EARTH);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Captured pointer events for API 26.
|
||||||
|
public boolean onCapturedPointerEvent(MotionEvent event)
|
||||||
|
{
|
||||||
|
int action = event.getActionMasked();
|
||||||
|
|
||||||
|
float x, y;
|
||||||
|
switch (action) {
|
||||||
|
case MotionEvent.ACTION_SCROLL:
|
||||||
|
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
|
||||||
|
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, false);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
case MotionEvent.ACTION_MOVE:
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
SDLActivity.onNativeMouse(0, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||||
|
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||||
|
|
||||||
|
// Change our action value to what SDL's code expects.
|
||||||
|
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
|
||||||
|
action = MotionEvent.ACTION_DOWN;
|
||||||
|
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
|
||||||
|
action = MotionEvent.ACTION_UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = event.getX(0);
|
||||||
|
y = event.getY(0);
|
||||||
|
int button = event.getButtonState();
|
||||||
|
|
||||||
|
SDLActivity.onNativeMouse(button, action, x, y, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Game</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Base application theme. -->
|
||||||
|
<style name="AppTheme" parent="android:Theme.NoTitleBar.Fullscreen">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:8.1.1'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
project(AudioPlayer)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
# Use pkg-config to find Vorbis
|
|
||||||
#find_package(PkgConfig REQUIRED)
|
|
||||||
#pkg_check_modules(VORBIS REQUIRED vorbis vorbisfile)
|
|
||||||
#pkg_check_modules(OGG REQUIRED ogg)
|
|
||||||
find_package(OpenAL REQUIRED)
|
|
||||||
|
|
||||||
add_library(audioplayer
|
|
||||||
src/AudioPlayer.cpp
|
|
||||||
include/AudioPlayer.hpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(audioplayer
|
|
||||||
PUBLIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
|
||||||
${OPENAL_INCLUDE_DIR}
|
|
||||||
${VORBIS_INCLUDE_DIRS}
|
|
||||||
${OGG_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(audioplayer
|
|
||||||
PUBLIC
|
|
||||||
${OPENAL_LIBRARY}
|
|
||||||
${VORBIS_LIBRARIES}
|
|
||||||
${OGG_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Test executable
|
|
||||||
add_executable(test_audio examples/test_audio.cpp)
|
|
||||||
target_link_libraries(test_audio PRIVATE audioplayer stdc++fs)
|
|
||||||
#git add ../../sounds
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#include "AudioPlayer.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
try {
|
|
||||||
AudioPlayer player;
|
|
||||||
const std::string filename = "Symphony No.6 (1st movement).ogg";
|
|
||||||
|
|
||||||
std::cout << "🔍 Looking for file: " << filename << " in sounds directory...\n";
|
|
||||||
|
|
||||||
if (!player.playFromSoundsDir(filename)) {
|
|
||||||
std::cout << "❌ Failed to play audio file\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "✅ Playing symphony...\n";
|
|
||||||
|
|
||||||
// Check status for 30 seconds
|
|
||||||
for (int i = 0; i < 30; ++i) {
|
|
||||||
std::cout << "📊 Status: " << (player.isPlaying() ? "Playing ▶️" : "Stopped ⏹️") << "\n";
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "❌ Error: " << e.what() << "\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <AL/al.h>
|
|
||||||
#include <AL/alc.h>
|
|
||||||
#include <vorbis/vorbisfile.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
class AudioPlayer {
|
|
||||||
public:
|
|
||||||
AudioPlayer();
|
|
||||||
~AudioPlayer();
|
|
||||||
|
|
||||||
// Для музыки с зацикливанием (если filename пустой - продолжает играть текущую)
|
|
||||||
bool playMusic(const std::string& filename = "");
|
|
||||||
|
|
||||||
// Для одноразовых звуковых эффектов
|
|
||||||
bool playSound(const std::string& filename);
|
|
||||||
|
|
||||||
void stop();
|
|
||||||
bool isPlaying() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ALCdevice* device;
|
|
||||||
ALCcontext* context;
|
|
||||||
ALuint musicSource; // Источник для музыки
|
|
||||||
ALuint soundSource; // Источник для звуков
|
|
||||||
ALuint musicBuffer; // Буфер для музыки
|
|
||||||
ALuint soundBuffer; // Буфер для звуков
|
|
||||||
bool playing;
|
|
||||||
std::string currentMusic; // Хранит имя текущего музыкального файла
|
|
||||||
|
|
||||||
std::vector<char> loadOgg(const std::string& filename, ALuint buffer);
|
|
||||||
std::string findFileInSounds(const std::string& filename);
|
|
||||||
bool isOggFile(const std::string& filename) const;
|
|
||||||
};
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
#include "AudioPlayer.hpp"
|
|
||||||
#include <filesystem>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
AudioPlayer::AudioPlayer() : device(nullptr), context(nullptr),
|
|
||||||
musicSource(0), soundSource(0), musicBuffer(0), soundBuffer(0), playing(false) {
|
|
||||||
device = alcOpenDevice(nullptr);
|
|
||||||
if (!device) {
|
|
||||||
throw std::runtime_error("Failed to open audio device");
|
|
||||||
}
|
|
||||||
|
|
||||||
context = alcCreateContext(device, nullptr);
|
|
||||||
if (!context) {
|
|
||||||
alcCloseDevice(device);
|
|
||||||
throw std::runtime_error("Failed to create audio context");
|
|
||||||
}
|
|
||||||
|
|
||||||
alcMakeContextCurrent(context);
|
|
||||||
alGenSources(1, &musicSource);
|
|
||||||
alGenSources(1, &soundSource);
|
|
||||||
alGenBuffers(1, &musicBuffer);
|
|
||||||
alGenBuffers(1, &soundBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioPlayer::~AudioPlayer() {
|
|
||||||
if (musicSource)
|
|
||||||
alDeleteSources(1, &musicSource);
|
|
||||||
if (soundSource)
|
|
||||||
alDeleteSources(1, &soundSource);
|
|
||||||
if (musicBuffer)
|
|
||||||
alDeleteBuffers(1, &musicBuffer);
|
|
||||||
if (soundBuffer)
|
|
||||||
alDeleteBuffers(1, &soundBuffer);
|
|
||||||
|
|
||||||
if (context) {
|
|
||||||
alcMakeContextCurrent(nullptr);
|
|
||||||
alcDestroyContext(context);
|
|
||||||
}
|
|
||||||
if (device)
|
|
||||||
alcCloseDevice(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioPlayer::isOggFile(const std::string& filename) const {
|
|
||||||
std::string ext = std::filesystem::path(filename).extension().string();
|
|
||||||
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
|
||||||
return ext == ".ogg";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string AudioPlayer::findFileInSounds(const std::string& filename) {
|
|
||||||
// Primary search path - "sounds" directory next to executable
|
|
||||||
std::filesystem::path soundsDir = std::filesystem::current_path() / "sounds";
|
|
||||||
|
|
||||||
// Alternative search paths
|
|
||||||
std::vector<std::filesystem::path> altPaths = {
|
|
||||||
std::filesystem::current_path() / ".." / "sounds", // One level up
|
|
||||||
std::filesystem::current_path() / ".." / ".." / "sounds", // Two levels up
|
|
||||||
"/home/albert/gay-jam/ZeptoLabTest1/sounds" // Absolute path
|
|
||||||
};
|
|
||||||
|
|
||||||
std::cout << "🔍 Searching for \"" << filename << "\" in:\n";
|
|
||||||
std::cout << " " << soundsDir << "\n";
|
|
||||||
|
|
||||||
if (std::filesystem::exists(soundsDir / filename)) {
|
|
||||||
return (soundsDir / filename).string();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try alternative paths
|
|
||||||
for (const auto& path : altPaths) {
|
|
||||||
std::cout << " " << path << "\n";
|
|
||||||
if (std::filesystem::exists(path / filename)) {
|
|
||||||
return (path / filename).string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("❌ File not found: " + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<char> AudioPlayer::loadOgg(const std::string& filename, ALuint buffer) {
|
|
||||||
FILE* file = fopen(filename.c_str(), "rb");
|
|
||||||
if (!file) {
|
|
||||||
throw std::runtime_error("Cannot open file: " + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
OggVorbis_File vf;
|
|
||||||
if (ov_open_callbacks(file, &vf, nullptr, 0, OV_CALLBACKS_DEFAULT) < 0) {
|
|
||||||
fclose(file);
|
|
||||||
throw std::runtime_error("Input not an Ogg file: " + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
vorbis_info* vi = ov_info(&vf, -1);
|
|
||||||
std::vector<char> audioData;
|
|
||||||
char data[4096];
|
|
||||||
int bitstream;
|
|
||||||
long bytes;
|
|
||||||
|
|
||||||
do {
|
|
||||||
bytes = ov_read(&vf, data, sizeof(data), 0, 2, 1, &bitstream);
|
|
||||||
if (bytes > 0) {
|
|
||||||
audioData.insert(audioData.end(), data, data + bytes);
|
|
||||||
}
|
|
||||||
} while (bytes > 0);
|
|
||||||
|
|
||||||
// Setup the buffer with the audio data
|
|
||||||
alBufferData(buffer,
|
|
||||||
(vi->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
|
|
||||||
audioData.data(),
|
|
||||||
audioData.size(),
|
|
||||||
vi->rate);
|
|
||||||
|
|
||||||
ov_clear(&vf);
|
|
||||||
return audioData;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioPlayer::playMusic(const std::string& filename) {
|
|
||||||
try {
|
|
||||||
// Если filename пустой, просто проверяем играет ли музыка
|
|
||||||
if (filename.empty()) {
|
|
||||||
if (!isPlaying()) {
|
|
||||||
alSourcei(musicSource, AL_LOOPING, AL_TRUE); // Включаем зацикливание
|
|
||||||
alSourcePlay(musicSource);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если filename не пустой, загружаем новую музыку
|
|
||||||
if (!isOggFile(filename)) {
|
|
||||||
std::cerr << "❌ Error: Music file must be an .ogg file\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string fullPath = findFileInSounds(filename);
|
|
||||||
std::cout << "✅ Found music file: " << fullPath << "\n";
|
|
||||||
|
|
||||||
// Останавливаем текущую музыку
|
|
||||||
alSourceStop(musicSource);
|
|
||||||
|
|
||||||
// Загружаем и настраиваем новую музыку
|
|
||||||
loadOgg(fullPath, musicBuffer);
|
|
||||||
alSourcei(musicSource, AL_BUFFER, musicBuffer);
|
|
||||||
alSourcei(musicSource, AL_LOOPING, AL_TRUE); // Включаем зацикливание
|
|
||||||
|
|
||||||
std::cout << "▶️ Starting music playback... " << musicSource << std::endl;
|
|
||||||
std::cout << "▶️ Music buffer... " << musicBuffer << std::endl;
|
|
||||||
alSourcePlay(musicSource);
|
|
||||||
|
|
||||||
currentMusic = filename;
|
|
||||||
playing = true;
|
|
||||||
return true;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "❌ Error playing music: " << e.what() << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioPlayer::playSound(const std::string& filename) {
|
|
||||||
try {
|
|
||||||
if (!isOggFile(filename)) {
|
|
||||||
std::cerr << "❌ Error: Sound file must be an .ogg file\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string fullPath = findFileInSounds(filename);
|
|
||||||
std::cout << "✅ Found sound file: " << fullPath << "\n";
|
|
||||||
|
|
||||||
// Загружаем и настраиваем звук
|
|
||||||
loadOgg(fullPath, soundBuffer);
|
|
||||||
alSourcei(soundSource, AL_BUFFER, soundBuffer);
|
|
||||||
alSourcei(soundSource, AL_LOOPING, AL_FALSE); // Выключаем зацикливание
|
|
||||||
|
|
||||||
std::cout << "▶️ Playing sound effect...\n";
|
|
||||||
alSourcePlay(soundSource);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "❌ Error playing sound: " << e.what() << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioPlayer::stop() {
|
|
||||||
alSourceStop(musicSource);
|
|
||||||
alSourceStop(soundSource);
|
|
||||||
playing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AudioPlayer::isPlaying() const {
|
|
||||||
ALint state;
|
|
||||||
alGetSourcei(musicSource, AL_SOURCE_STATE, &state);
|
|
||||||
return state == AL_PLAYING;
|
|
||||||
}
|
|
||||||
-12146
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,17 @@
|
|||||||
|
# Project-wide Gradle settings.
|
||||||
|
|
||||||
|
# IDE (e.g. Android Studio) users:
|
||||||
|
# Gradle settings configured through the IDE *will override*
|
||||||
|
# any settings specified in this file.
|
||||||
|
|
||||||
|
# For more details on how to configure your build environment visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
|
|
||||||
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
|
|
||||||
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
# org.gradle.parallel=true
|
||||||
Vendored
BIN
Binary file not shown.
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#Thu Nov 11 18:20:34 PST 2021
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS=""
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn ( ) {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die ( ) {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin, switch paths to Windows format before running java
|
||||||
|
if $cygwin ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|grep -E -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|grep -E -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
(0) set -- ;;
|
||||||
|
(1) set -- "$args0" ;;
|
||||||
|
(2) set -- "$args0" "$args1" ;;
|
||||||
|
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||||
|
function splitJvmOpts() {
|
||||||
|
JVM_OPTS=("$@")
|
||||||
|
}
|
||||||
|
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||||
|
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||||
Vendored
+90
@@ -0,0 +1,90 @@
|
|||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS=
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto init
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:init
|
||||||
|
@rem Get command-line arguments, handling Windowz variants
|
||||||
|
|
||||||
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
|
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||||
|
|
||||||
|
:win9xME_args
|
||||||
|
@rem Slurp the command line arguments.
|
||||||
|
set CMD_LINE_ARGS=
|
||||||
|
set _SKIP=2
|
||||||
|
|
||||||
|
:win9xME_args_slurp
|
||||||
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
|
set CMD_LINE_ARGS=%*
|
||||||
|
goto execute
|
||||||
|
|
||||||
|
:4NT_args
|
||||||
|
@rem Get arguments from the 4NT Shell from JP Software
|
||||||
|
set CMD_LINE_ARGS=%$
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
#include "Game.h"
|
|
||||||
#include "Environment.h"
|
|
||||||
ZL::Game game;
|
|
||||||
void MainLoop() {
|
|
||||||
game.update();
|
|
||||||
}
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
constexpr int CONST_WIDTH = 1280;
|
|
||||||
constexpr int CONST_HEIGHT = 720;
|
|
||||||
|
|
||||||
ZL::Environment::width = CONST_WIDTH;
|
|
||||||
ZL::Environment::height = CONST_HEIGHT;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
|
|
||||||
std::cerr << "SDL_Init failed: " << SDL_GetError() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
|
|
||||||
|
|
||||||
SDL_Window* win = SDL_CreateWindow("Space Ship Game",
|
|
||||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
||||||
CONST_WIDTH, CONST_HEIGHT,
|
|
||||||
SDL_WINDOW_OPENGL);
|
|
||||||
|
|
||||||
if (!win) {
|
|
||||||
std::cerr << "SDL_CreateWindow failed: " << SDL_GetError() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GLContext glContext = SDL_GL_CreateContext(win);
|
|
||||||
if (!glContext) {
|
|
||||||
std::cerr << "SDL_GL_CreateContext failed: " << SDL_GetError() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Привязка контекста к окну — важно!
|
|
||||||
SDL_GL_MakeCurrent(win, glContext);
|
|
||||||
|
|
||||||
ZL::Environment::window = win;
|
|
||||||
#else
|
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
|
|
||||||
SDL_Log("SDL init failed: %s", SDL_GetError());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
|
||||||
|
|
||||||
ZL::Environment::window = SDL_CreateWindow(
|
|
||||||
"Space Ship Game",
|
|
||||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
||||||
CONST_WIDTH, CONST_HEIGHT,
|
|
||||||
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
|
|
||||||
);
|
|
||||||
|
|
||||||
SDL_GLContext ctx = SDL_GL_CreateContext(ZL::Environment::window);
|
|
||||||
SDL_GL_MakeCurrent(ZL::Environment::window, ctx);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
game.setup();
|
|
||||||
|
|
||||||
#ifdef EMSCRIPTEN
|
|
||||||
emscripten_set_main_loop(MainLoop, 0, 1);
|
|
||||||
#else
|
|
||||||
while (!game.shouldExit()) {
|
|
||||||
game.update();
|
|
||||||
SDL_Delay(2);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
include ':app'
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.31402.337
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "space-game001", "space-game001.vcxproj", "{400A41AE-F904-4D49-9DBC-95934D0EF82E}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Debug|x86.ActiveCfg = Debug|Win32
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Debug|x86.Build.0 = Debug|Win32
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Release|x64.Build.0 = Release|x64
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Release|x86.ActiveCfg = Release|Win32
|
|
||||||
{400A41AE-F904-4D49-9DBC-95934D0EF82E}.Release|x86.Build.0 = Release|Win32
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {C3D1B89D-D5F1-4E33-8439-56D6C43B07A2}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<VCProjectVersion>16.0</VCProjectVersion>
|
|
||||||
<Keyword>Win32Proj</Keyword>
|
|
||||||
<ProjectGuid>{400a41ae-f904-4d49-9dbc-95934d0ef82e}</ProjectGuid>
|
|
||||||
<RootNamespace>ZeptoLabTest1</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
|
||||||
<ProjectName>space-game001</ProjectName>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v143</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="Shared">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
<TargetName>Viola</TargetName>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>PNG_ENABLED;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<AdditionalIncludeDirectories>..\SDL2-2.28.3\include;..\lpng1645\build\install\include;C:\Work\OpenAL 1.1 SDK\include;..\Projects\libogg\include;..\vorbis\include</AdditionalIncludeDirectories>
|
|
||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<AdditionalDependencies>libpng16_staticd.lib;SDL2.lib;SDL2main.lib;opengl32.lib;glu32.lib;shell32.lib;opengl32.lib;glu32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib</AdditionalDependencies>
|
|
||||||
<AdditionalLibraryDirectories>..\SDL2-2.28.3\lib\x64;..\lpng1645\build\install\lib</AdditionalLibraryDirectories>
|
|
||||||
<EntryPointSymbol>
|
|
||||||
</EntryPointSymbol>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<SDLCheck>true</SDLCheck>
|
|
||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<ConformanceMode>true</ConformanceMode>
|
|
||||||
<AdditionalIncludeDirectories>C:\Work\SDL2-2.28.3\include;C:\Work\Projects\lpng1645\build\install\include;C:\Work\OpenAL 1.1 SDK\include;C:\Work\Projects\libogg\include;C:\Work\Projects\vorbis\include</AdditionalIncludeDirectories>
|
|
||||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Windows</SubSystem>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<AdditionalDependencies>libogg_static.lib;audioplayer.lib;vorbisfile.lib;vorbis.lib;OpenAL32.lib;SDL2.lib;SDL2main.lib;opengl32.lib;glu32.lib;shell32.lib;opengl32.lib;glu32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib</AdditionalDependencies>
|
|
||||||
<AdditionalLibraryDirectories>C:\Work\Projects\ZeptoLabTest1\cmakeaudioplayer\build\Release;C:\Work\SDL2-2.28.3\lib\x64;C:\Work\Projects\vorbis\build\lib\Release;C:\Work\OpenAL 1.1 SDK\libs\Win64;C:\Work\Projects\libogg\win32\VS2010\x64\Release</AdditionalLibraryDirectories>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="AudioPlayerAsync.cpp" />
|
|
||||||
<ClCompile Include="BoneAnimatedModel.cpp" />
|
|
||||||
<ClCompile Include="Environment.cpp" />
|
|
||||||
<ClCompile Include="Game.cpp" />
|
|
||||||
<ClCompile Include="main.cpp" />
|
|
||||||
<ClCompile Include="Math.cpp" />
|
|
||||||
<ClCompile Include="OpenGlExtensions.cpp" />
|
|
||||||
<ClCompile Include="Renderer.cpp" />
|
|
||||||
<ClCompile Include="ShaderManager.cpp" />
|
|
||||||
<ClCompile Include="TextModel.cpp" />
|
|
||||||
<ClCompile Include="TextureManager.cpp" />
|
|
||||||
<ClCompile Include="Utils.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="AnimatedModel.h" />
|
|
||||||
<ClInclude Include="AudioPlayerAsync.h" />
|
|
||||||
<ClInclude Include="BoneAnimatedModel.h" />
|
|
||||||
<ClInclude Include="Environment.h" />
|
|
||||||
<ClInclude Include="Game.h" />
|
|
||||||
<ClInclude Include="Math.h" />
|
|
||||||
<ClInclude Include="OpenGlExtensions.h" />
|
|
||||||
<ClInclude Include="Renderer.h" />
|
|
||||||
<ClInclude Include="ShaderManager.h" />
|
|
||||||
<ClInclude Include="TextModel.h" />
|
|
||||||
<ClInclude Include="TextureManager.h" />
|
|
||||||
<ClInclude Include="Utils.h" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<Filter Include="Source Files">
|
|
||||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
|
||||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Header Files">
|
|
||||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
|
||||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
|
||||||
</Filter>
|
|
||||||
<Filter Include="Resource Files">
|
|
||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
|
||||||
</Filter>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="main.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="TextureManager.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Renderer.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="ShaderManager.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="OpenGlExtensions.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Math.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Utils.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Game.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="BoneAnimatedModel.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="TextModel.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Environment.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="AudioPlayerAsync.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClInclude Include="TextureManager.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Renderer.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="ShaderManager.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="OpenGlExtensions.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Math.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Utils.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Game.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="AnimatedModel.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="BoneAnimatedModel.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="TextModel.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Environment.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="AudioPlayerAsync.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
g++ Game.cpp main.cpp Math.cpp OpenGlExtensions.cpp Physics.cpp Renderer.cpp \
|
|
||||||
ShaderManager.cpp TextureManager.cpp Utils.cpp BoneAnimatedModel.cpp \
|
|
||||||
ObjLoader.cpp cmakeaudioplayer/src/AudioPlayer.cpp TextModel.cpp \
|
|
||||||
Inventory.cpp Environment.cpp GameObjectManager.cpp RenderSystem.cpp QuestScripts.cpp \
|
|
||||||
AudioPlayerAsync.cpp \
|
|
||||||
-o sdl_app -O2 -std=c++17 -pthread \
|
|
||||||
-I cmakeaudioplayer/include \
|
|
||||||
$(pkg-config --cflags --libs sdl2 gl) \
|
|
||||||
$(pkg-config --cflags --libs vorbis vorbisfile ogg) \
|
|
||||||
-lopenal
|
|
||||||
|
|
||||||
#./sdl_app
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
BasedOnStyle: LLVM
|
|
||||||
IndentWidth: 4
|
|
||||||
ColumnLimit: 2000
|
|
||||||
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
MaxEmptyLinesToKeep: 2
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BraceWrapping:
|
|
||||||
BeforeElse: true
|
|
||||||
AlignEscapedNewlines: Left
|
|
||||||
UseTab: Never
|
|
||||||
#PPDirectiveIndentStyle: AfterHash
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug Report
|
|
||||||
about: Report where libzip didn't behave like you expected.
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the Bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**Expected Behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Observed Behavior**
|
|
||||||
A clear and concise description of what actually happened.
|
|
||||||
|
|
||||||
**To Reproduce**
|
|
||||||
Short program or code snippet that reproduces the problem.
|
|
||||||
|
|
||||||
**libzip Version**
|
|
||||||
Version of libzip or revision repository used.
|
|
||||||
|
|
||||||
**Operating System**
|
|
||||||
Operating system and version, used compiler.
|
|
||||||
|
|
||||||
**Test Files**
|
|
||||||
If applicable, attach and describe zip archives that trigger the problem.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
name: Compile Error
|
|
||||||
about: Report when libzip does not compile.
|
|
||||||
title: ''
|
|
||||||
labels: compile
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Compiler Error**
|
|
||||||
Output from the compiler, including exact and complete error message, file name and line number.
|
|
||||||
|
|
||||||
**libzip Version**
|
|
||||||
Version of libzip or revision repository used.
|
|
||||||
|
|
||||||
**Operating System and Compiler**
|
|
||||||
The operating system and compiler used, including version number.
|
|
||||||
|
|
||||||
Also, any flags passed to `cmake`.
|
|
||||||
|
|
||||||
**Autodetected Configuration**
|
|
||||||
Attach `CmakeCache.txt` from your build directory. This list everything `cmake` detected on your system.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the problem here.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature Request
|
|
||||||
about: Suggest an idea for this project.
|
|
||||||
title: ''
|
|
||||||
labels: enhancement
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Description**
|
|
||||||
A clear and concise description of what you want to achieve, why the current features are insufficient, and why you think it is generally useful.
|
|
||||||
|
|
||||||
Also, have you checked whether the feature is already mentioned in TODO.md? If so, only submit a new issue if you expand on it.
|
|
||||||
|
|
||||||
**Solution**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context about the feature request here.
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
name: Other
|
|
||||||
about: If you have a question about libzip , consider using Discussions instead.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
name: CIFuzz
|
|
||||||
on: [pull_request]
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
jobs:
|
|
||||||
Fuzzing:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Build Fuzzers
|
|
||||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
|
||||||
with:
|
|
||||||
oss-fuzz-project-name: 'libzip'
|
|
||||||
dry-run: false
|
|
||||||
- name: Run Fuzzers
|
|
||||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
|
||||||
with:
|
|
||||||
oss-fuzz-project-name: 'libzip'
|
|
||||||
fuzz-seconds: 600
|
|
||||||
dry-run: false
|
|
||||||
- name: Upload Crash
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
name: artifacts
|
|
||||||
path: ./out/artifacts
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
name: BSD
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
NetBSD:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: NetBSD test
|
|
||||||
uses: vmactions/netbsd-vm@v1
|
|
||||||
with:
|
|
||||||
usesh: true
|
|
||||||
copyback: false
|
|
||||||
prepare: |
|
|
||||||
/usr/sbin/pkg_add cmake zstd py313-pip
|
|
||||||
/usr/pkg/bin/pip-3.13 install nihtest
|
|
||||||
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#adding-a-system-path
|
|
||||||
echo "/usr/pkg/bin" >> "$GITHUB_PATH"
|
|
||||||
run: |
|
|
||||||
cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
cmake ${{ matrix.cmake_extra }} ${{github.workspace}}
|
|
||||||
cmake --build . --config Release
|
|
||||||
ctest --output-on-failure -V -C Release
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
name: build
|
|
||||||
on: [push]
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
jobs:
|
|
||||||
all:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
name: ${{ matrix.os }}${{ matrix.name_extra }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
|
||||||
cmake_extra: [""]
|
|
||||||
name_extra: [""]
|
|
||||||
include:
|
|
||||||
- os: windows-latest
|
|
||||||
cmake_extra: "-T ClangCl"
|
|
||||||
name_extra: " clang-cl"
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: install python and pip
|
|
||||||
uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: '3.11'
|
|
||||||
- name: install dependencies (Linux)
|
|
||||||
if: ${{ runner.os == 'Linux' }}
|
|
||||||
run: |
|
|
||||||
sudo apt-get install libzstd-dev
|
|
||||||
- name: install latest CMake and Ninja for lukka/run-vcpkg (Windows)
|
|
||||||
if: ${{ runner.os == 'Windows' }}
|
|
||||||
uses: lukka/get-cmake@latest
|
|
||||||
- name: install dependencies (Windows)
|
|
||||||
if: ${{ runner.os == 'Windows' }}
|
|
||||||
uses: lukka/run-vcpkg@v11
|
|
||||||
with:
|
|
||||||
vcpkgGitCommitId: 2cf957350da28ad032178a974607f59f961217d9
|
|
||||||
- name: prepare build directory and install nihtest
|
|
||||||
run: |
|
|
||||||
cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
pip install nihtest
|
|
||||||
- name: configure (Unix)
|
|
||||||
if: ${{ runner.os != 'Windows' }}
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
cmake ${{ matrix.cmake_extra }} ${{github.workspace}}
|
|
||||||
- name: configure (Windows)
|
|
||||||
if: ${{ runner.os == 'Windows' }}
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
cmake ${{ matrix.cmake_extra }} -DCMAKE_TOOLCHAIN_FILE=${{env.VCPKG_ROOT}}/scripts/buildsystems/vcpkg.cmake ${{github.workspace}}
|
|
||||||
- name: build
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
cmake --build . --config Release
|
|
||||||
- name: Archive production artifacts
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: regress-directory-${{ matrix.os }}-${{ matrix.name_extra }}
|
|
||||||
path: |
|
|
||||||
${{runner.workspace}}/build/regress
|
|
||||||
- name: test
|
|
||||||
working-directory: ${{runner.workspace}}/build
|
|
||||||
run: |
|
|
||||||
ctest --output-on-failure -V -C Release
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
# For most projects, this workflow file will not need changing; you simply need
|
|
||||||
# to commit it to your repository.
|
|
||||||
#
|
|
||||||
# You may wish to alter this file to override the set of languages analyzed,
|
|
||||||
# or to provide custom queries or build logic.
|
|
||||||
name: "CodeQL"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
pull_request:
|
|
||||||
# The branches below must be a subset of the branches above
|
|
||||||
branches: [master]
|
|
||||||
schedule:
|
|
||||||
- cron: '0 10 * * 4'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
# Override automatic language detection by changing the below list
|
|
||||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
|
||||||
language: ['cpp']
|
|
||||||
# Learn more...
|
|
||||||
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
# We must fetch at least the immediate parents so that if this is
|
|
||||||
# a pull request then we can checkout the head.
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v3
|
|
||||||
with:
|
|
||||||
languages: ${{ matrix.language }}
|
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
||||||
# By default, queries listed here will override any specified in a config file.
|
|
||||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
||||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v3
|
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
|
||||||
# 📚 https://git.io/JvXDl
|
|
||||||
|
|
||||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
|
||||||
# and modify them (or add more) to build your code if your project
|
|
||||||
# uses a compiled language
|
|
||||||
|
|
||||||
#- run: |
|
|
||||||
# make bootstrap
|
|
||||||
# make release
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v3
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
name: Coverity
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 3 * * 1'
|
|
||||||
# Mondays at 03:00
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Coverity
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment: coverity
|
|
||||||
|
|
||||||
env:
|
|
||||||
TOKEN: ${{ secrets.COVERITY_TOKEN }}
|
|
||||||
PROJECT: libzip
|
|
||||||
SHORT_PROJECT: libzip
|
|
||||||
EMAIL: wiz@gatalith.at
|
|
||||||
COV_TOOLS: cov-tools
|
|
||||||
COV_RESULTS: cov-int
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check Secret
|
|
||||||
run: |
|
|
||||||
[ -n "${{ secrets.COVERITY_TOKEN }}" ]
|
|
||||||
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Install Dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get install libzstd-dev
|
|
||||||
|
|
||||||
- name: Configure
|
|
||||||
run: |
|
|
||||||
cmake -E make_directory ${{runner.workspace}}/build
|
|
||||||
cmake ${{ matrix.cmake_extra }} ${{github.workspace}}
|
|
||||||
|
|
||||||
- name: Download Coverity
|
|
||||||
run: |
|
|
||||||
wget --quiet https://scan.coverity.com/download/linux64 --post-data "token=$TOKEN&project=$PROJECT" -O "$COV_TOOLS.tar.gz"
|
|
||||||
mkdir "$COV_TOOLS"
|
|
||||||
tar xzf "$COV_TOOLS.tar.gz" --strip 1 -C "$COV_TOOLS"
|
|
||||||
ls -l "$COV_TOOLS"
|
|
||||||
|
|
||||||
- name: Build with Coverity
|
|
||||||
run: |
|
|
||||||
export PATH="$(pwd)/$COV_TOOLS/bin:$PATH"
|
|
||||||
cov-build --dir $COV_RESULTS make -j ${{steps.cpu-cores.outputs.count}}
|
|
||||||
# Filter out private info
|
|
||||||
sed -E -i 's/TOKEN=([-_A-Za-z0-9]+)/TOKEN=XXX/g' cov-int/build-log.txt
|
|
||||||
|
|
||||||
- name: Upload build log
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: build-log
|
|
||||||
path: cov-int/build-log.txt
|
|
||||||
retention-days: 10
|
|
||||||
|
|
||||||
- name: Submit Results
|
|
||||||
run: |
|
|
||||||
tar -czf $SHORT_PROJECT.tgz $COV_RESULTS
|
|
||||||
ls -lh $SHORT_PROJECT.tgz
|
|
||||||
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
||||||
GIT_HASH="$(git rev-parse --short HEAD)"
|
|
||||||
echo "HASH: $GIT_HASH"
|
|
||||||
GIT_DESC="$(git log -n1 --format="%s" $GIT_HASH)"
|
|
||||||
echo "DESC: $GIT_DESC"
|
|
||||||
curl --fail --output curl.log \
|
|
||||||
--form token=$TOKEN \
|
|
||||||
--form email=$EMAIL \
|
|
||||||
--form file=@$SHORT_PROJECT.tgz \
|
|
||||||
--form version="$GIT_HASH" \
|
|
||||||
--form description="$GIT_DESC" \
|
|
||||||
https://scan.coverity.com/builds?project=$PROJECT
|
|
||||||
# If we go over quota, alert the user
|
|
||||||
cat curl.log
|
|
||||||
grep -qv "quota.*reached" curl.log || false
|
|
||||||
|
|
||||||
-174
@@ -1,174 +0,0 @@
|
|||||||
# libzip API changes
|
|
||||||
|
|
||||||
This file describes changes in the libzip API and how to adapt your
|
|
||||||
code for them.
|
|
||||||
|
|
||||||
You can define `ZIP_DISABLE_DEPRECATED` before including `<zip.h>` to hide
|
|
||||||
prototypes for deprecated functions, to find out about functions that
|
|
||||||
might be removed at some point.
|
|
||||||
|
|
||||||
## Changed in libzip-1.10.0
|
|
||||||
|
|
||||||
### deprecated `zip_source_zip` and `zip_source_zip_create`
|
|
||||||
|
|
||||||
These functions were replaced with `zip_source_zip_file` and `zip_source_zip_file_create`. The implicit handling of the flag `ZIP_FL_COMPRESSED` was removed, the flag can now be specified explicitly.
|
|
||||||
|
|
||||||
If you want to get the compressed data for the whole file, use
|
|
||||||
|
|
||||||
```C
|
|
||||||
zip_source_zip_file(za, source_archive, source_index, ZIP_FL_COMPRESSED, 0, -1, NULL)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Changed in libzip-1.0
|
|
||||||
|
|
||||||
### new type `zip_error_t`
|
|
||||||
|
|
||||||
Error information is stored in the newly public type `zip_error_t`. Use
|
|
||||||
this to access information about an error, instead of the deprecated
|
|
||||||
functions that operated on two ints.
|
|
||||||
|
|
||||||
deprecated functions:
|
|
||||||
- `zip_error_get_sys_type()`
|
|
||||||
- `zip_error_get()`
|
|
||||||
- `zip_error_to_str()`
|
|
||||||
- `zip_file_error_get()`
|
|
||||||
|
|
||||||
See their man pages for instructions on how to replace them.
|
|
||||||
|
|
||||||
The most common affected use is `zip_open`. The new recommended usage
|
|
||||||
is:
|
|
||||||
|
|
||||||
```c
|
|
||||||
int err;
|
|
||||||
if ((za = zip_open(archive, flags, &err)) == NULL) {
|
|
||||||
zip_error_t error;
|
|
||||||
zip_error_init_with_code(&error, err);
|
|
||||||
fprintf(stderr, "can't open zip archive '%s': %s\n", archive, zip_error_strerror(&error));
|
|
||||||
zip_error_fini(&error);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### more typedefs
|
|
||||||
|
|
||||||
The following typedefs have been added for better readability:
|
|
||||||
|
|
||||||
```c
|
|
||||||
typedef struct zip zip_t;
|
|
||||||
typedef struct zip_file zip_file_t;
|
|
||||||
typedef struct zip_source zip_source_t;
|
|
||||||
typedef struct zip_stat zip_stat_t;
|
|
||||||
```
|
|
||||||
|
|
||||||
This means you can use "`zip_t`" instead of "`struct zip`", etc.
|
|
||||||
|
|
||||||
|
|
||||||
### torrentzip support removed
|
|
||||||
|
|
||||||
torrentzip depends on a particular zlib version which is by now quite
|
|
||||||
old.
|
|
||||||
|
|
||||||
## Changed in libzip-0.11
|
|
||||||
|
|
||||||
### new type `zip_flags_t`
|
|
||||||
|
|
||||||
The functions which have flags now use the `zip_flags_t` type for this.
|
|
||||||
All old flags fit; you need only to adapt code if you were saving flags in a
|
|
||||||
local variable. Use `zip_flags_t` for such a variable.
|
|
||||||
This affects:
|
|
||||||
- `zip_fopen()`
|
|
||||||
- `zip_fopen_encrypted()`
|
|
||||||
- `zip_fopen_index()`
|
|
||||||
- `zip_fopen_index_encrypted()`
|
|
||||||
- `zip_get_archive_comment()`
|
|
||||||
- `zip_get_archive_flag()`
|
|
||||||
- `zip_get_num_entries()`
|
|
||||||
- `zip_get_name()`
|
|
||||||
- `zip_name_locate()`
|
|
||||||
- `zip_set_archive_flag()`
|
|
||||||
- `zip_source_zip()`
|
|
||||||
- `zip_stat()`
|
|
||||||
- `zip_stat_index()`
|
|
||||||
|
|
||||||
#### `ZIP_FL_*`, `ZIP_AFL_*`, `ZIP_STAT_*` are now unsigned constants
|
|
||||||
|
|
||||||
To match the new `zip_flags_t` type.
|
|
||||||
|
|
||||||
#### `zip_add()`, `zip_add_dir()`
|
|
||||||
|
|
||||||
These functions were replaced with `zip_file_add()` and `zip_dir_add()`, respectively,
|
|
||||||
to add a flags argument.
|
|
||||||
|
|
||||||
#### `zip_rename()`, `zip_replace()`
|
|
||||||
|
|
||||||
These functions were replaced with `zip_file_rename()` and `zip_file_replace()`,
|
|
||||||
respectively, to add a flags argument.
|
|
||||||
|
|
||||||
#### `zip_get_file_comment()`
|
|
||||||
|
|
||||||
This function was replaced with `zip_file_get_comment()`; one argument was promoted from
|
|
||||||
`int` to `zip_uint32_t`, the other is now a `zip_flags_t`.
|
|
||||||
|
|
||||||
#### `zip_set_file_comment()`
|
|
||||||
|
|
||||||
This function was replaced with `zip_file_set_comment()`; an argument was promoted from
|
|
||||||
`int` to `zip_uint16_t`, and a `zip_flags_t` argument was added.
|
|
||||||
|
|
||||||
### integer type size changes
|
|
||||||
|
|
||||||
Some argument and return values were not the right size or sign.
|
|
||||||
|
|
||||||
#### `zip_name_locate()`
|
|
||||||
|
|
||||||
The return value was `int`, which can be too small. The function now returns `zip_int64_t`.
|
|
||||||
|
|
||||||
|
|
||||||
#### `zip_get_num_entries()`
|
|
||||||
|
|
||||||
The return type is now signed, to allow signaling errors.
|
|
||||||
|
|
||||||
#### `zip_set_archive_comment()`
|
|
||||||
|
|
||||||
The last argument changed from `int` to `zip_uint16_t`.
|
|
||||||
|
|
||||||
### extra field handling rewritten
|
|
||||||
|
|
||||||
The `zip_get_file_extra()` and `zip_set_file_extra()` functions were removed.
|
|
||||||
They only worked on the whole extra field set.
|
|
||||||
|
|
||||||
Instead, you can now set, get, count, and delete each extra field separately,
|
|
||||||
using the functions:
|
|
||||||
- `zip_file_extra_field_delete()`
|
|
||||||
- `zip_file_extra_field_delete_by_id()`
|
|
||||||
- `zip_file_extra_field_get()`
|
|
||||||
- `zip_file_extra_field_get_by_id()`
|
|
||||||
- `zip_file_extra_fields_count()`
|
|
||||||
- `zip_file_extra_fields_count_by_id()`
|
|
||||||
- `zip_file_extra_field_set()`
|
|
||||||
|
|
||||||
Please read the corresponding man pages for details.
|
|
||||||
|
|
||||||
### new functions
|
|
||||||
|
|
||||||
#### `zip_discard()`
|
|
||||||
|
|
||||||
The new `zip_discard()` function closes an archive without committing the
|
|
||||||
scheduled changes.
|
|
||||||
|
|
||||||
#### `zip_set_file_compression()`
|
|
||||||
|
|
||||||
The new `zip_set_file_compression()` function allows setting compression
|
|
||||||
levels for files.
|
|
||||||
|
|
||||||
### argument changes
|
|
||||||
|
|
||||||
#### file names
|
|
||||||
|
|
||||||
File names arguments are now allowed to be `NULL` to have an empty file name.
|
|
||||||
This mostly affects `zip_file_add()`, `zip_dir_add()`, and `zip_file_rename()`.
|
|
||||||
|
|
||||||
For `zip_get_name()`, `zip_file_get_comment()`, and `zip_get_archive_comment()`, if
|
|
||||||
the file name or comment is empty, a string of length 0 is returned.
|
|
||||||
`NULL` is returned for errors only.
|
|
||||||
|
|
||||||
Previously, `NULL` was returned for empty/unset file names and comments and
|
|
||||||
errors, leaving no way to differentiate between the two.
|
|
||||||
Vendored
-2
@@ -1,2 +0,0 @@
|
|||||||
Dieter Baron <dillo@nih.at>
|
|
||||||
Thomas Klausner <wiz@gatalith.at>
|
|
||||||
-499
@@ -1,499 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
|
||||||
if (${CMAKE_VERSION} VERSION_LESS "3.17.0")
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake-compat)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
project(libzip
|
|
||||||
VERSION 1.11.3
|
|
||||||
LANGUAGES C)
|
|
||||||
|
|
||||||
if(NOT libzip_VERSION_PATCH)
|
|
||||||
set(libzip_VERSION_PATCH 0)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(ENABLE_COMMONCRYPTO "Enable use of CommonCrypto" ON)
|
|
||||||
option(ENABLE_GNUTLS "Enable use of GnuTLS" ON)
|
|
||||||
option(ENABLE_MBEDTLS "Enable use of mbed TLS" ON)
|
|
||||||
option(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
|
|
||||||
option(ENABLE_WINDOWS_CRYPTO "Enable use of Windows cryptography libraries" ON)
|
|
||||||
|
|
||||||
option(ENABLE_BZIP2 "Enable use of BZip2" ON)
|
|
||||||
option(ENABLE_LZMA "Enable use of LZMA" ON)
|
|
||||||
option(ENABLE_ZSTD "Enable use of Zstandard" ON)
|
|
||||||
|
|
||||||
option(ENABLE_FDOPEN "Enable zip_fdopen, which is not allowed in Microsoft CRT secure libraries" ON)
|
|
||||||
|
|
||||||
option(BUILD_TOOLS "Build tools in the src directory (zipcmp, zipmerge, ziptool)" ON)
|
|
||||||
option(BUILD_REGRESS "Build regression tests" ON)
|
|
||||||
option(BUILD_OSSFUZZ "Build fuzzers for ossfuzz" ON)
|
|
||||||
option(BUILD_EXAMPLES "Build examples" ON)
|
|
||||||
option(BUILD_DOC "Build documentation" ON)
|
|
||||||
|
|
||||||
include(CheckFunctionExists)
|
|
||||||
include(CheckIncludeFiles)
|
|
||||||
include(CheckLibraryExists)
|
|
||||||
include(CheckSymbolExists)
|
|
||||||
include(CheckTypeSize)
|
|
||||||
include(CheckCSourceRuns)
|
|
||||||
include(CheckCSourceCompiles)
|
|
||||||
include(CheckStructHasMember)
|
|
||||||
include(TestBigEndian)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
if(ENABLE_COMMONCRYPTO)
|
|
||||||
check_include_files(CommonCrypto/CommonCrypto.h COMMONCRYPTO_FOUND)
|
|
||||||
endif()
|
|
||||||
if(ENABLE_GNUTLS)
|
|
||||||
find_package(Nettle 3.0)
|
|
||||||
find_package(GnuTLS)
|
|
||||||
endif()
|
|
||||||
if(ENABLE_MBEDTLS)
|
|
||||||
find_package(MbedTLS 1.0)
|
|
||||||
endif()
|
|
||||||
if(ENABLE_OPENSSL)
|
|
||||||
find_package(OpenSSL)
|
|
||||||
endif()
|
|
||||||
if(WIN32)
|
|
||||||
if(ENABLE_WINDOWS_CRYPTO)
|
|
||||||
set(WINDOWS_CRYPTO_FOUND TRUE)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
|
|
||||||
option(LIBZIP_DO_INSTALL "Install libzip and the related files" ON)
|
|
||||||
|
|
||||||
option(SHARED_LIB_VERSIONNING "Add SO version in .so build" ON)
|
|
||||||
|
|
||||||
find_program(MDOCTOOL NAMES mandoc groff)
|
|
||||||
if (MDOCTOOL)
|
|
||||||
set(DOCUMENTATION_FORMAT "mdoc" CACHE STRING "Documentation format")
|
|
||||||
else()
|
|
||||||
find_program(MANTOOL NAMES nroff)
|
|
||||||
if (MANTOOL)
|
|
||||||
set(DOCUMENTATION_FORMAT "man" CACHE STRING "Documentation format")
|
|
||||||
else()
|
|
||||||
set(DOCUMENTATION_FORMAT "html" CACHE STRING "Documentation format")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(Dist)
|
|
||||||
Dist(${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION})
|
|
||||||
|
|
||||||
#ADD_CUSTOM_TARGET(uninstall
|
|
||||||
# COMMAND cat ${PROJECT_BINARY_DIR}/install_manifest.txt | xargs rm
|
|
||||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
# )
|
|
||||||
|
|
||||||
if(BUILD_SHARED_LIBS)
|
|
||||||
set(HAVE_SHARED TRUE)
|
|
||||||
else()
|
|
||||||
set(ZIP_STATIC TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Checks
|
|
||||||
|
|
||||||
# Request ISO C secure library functions (memcpy_s &c)
|
|
||||||
list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__STDC_WANT_LIB_EXT1__=1)
|
|
||||||
|
|
||||||
check_function_exists(_close HAVE__CLOSE)
|
|
||||||
check_function_exists(_dup HAVE__DUP)
|
|
||||||
check_function_exists(_fdopen HAVE__FDOPEN)
|
|
||||||
check_function_exists(_fileno HAVE__FILENO)
|
|
||||||
check_function_exists(_fseeki64 HAVE__FSEEKI64)
|
|
||||||
check_function_exists(_fstat64 HAVE__FSTAT64)
|
|
||||||
check_function_exists(_setmode HAVE__SETMODE)
|
|
||||||
check_function_exists(_stat64 HAVE__STAT64)
|
|
||||||
check_symbol_exists(_snprintf stdio.h HAVE__SNPRINTF)
|
|
||||||
check_symbol_exists(_snprintf_s stdio.h HAVE__SNPRINTF_S)
|
|
||||||
check_symbol_exists(_snwprintf_s stdio.h HAVE__SNWPRINTF_S)
|
|
||||||
check_function_exists(_strdup HAVE__STRDUP)
|
|
||||||
check_symbol_exists(_stricmp string.h HAVE__STRICMP)
|
|
||||||
check_function_exists(_strtoi64 HAVE__STRTOI64)
|
|
||||||
check_function_exists(_strtoui64 HAVE__STRTOUI64)
|
|
||||||
check_function_exists(_unlink HAVE__UNLINK)
|
|
||||||
check_function_exists(arc4random HAVE_ARC4RANDOM)
|
|
||||||
check_function_exists(clonefile HAVE_CLONEFILE)
|
|
||||||
check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO)
|
|
||||||
check_function_exists(explicit_memset HAVE_EXPLICIT_MEMSET)
|
|
||||||
check_function_exists(fchmod HAVE_FCHMOD)
|
|
||||||
check_function_exists(fileno HAVE_FILENO)
|
|
||||||
check_function_exists(fseeko HAVE_FSEEKO)
|
|
||||||
check_function_exists(ftello HAVE_FTELLO)
|
|
||||||
check_function_exists(getprogname HAVE_GETPROGNAME)
|
|
||||||
check_function_exists(GetSecurityInfo HAVE_GETSECURITYINFO)
|
|
||||||
check_symbol_exists(localtime_r time.h HAVE_LOCALTIME_R)
|
|
||||||
check_symbol_exists(localtime_s time.h HAVE_LOCALTIME_S)
|
|
||||||
check_function_exists(memcpy_s HAVE_MEMCPY_S)
|
|
||||||
check_function_exists(random HAVE_RANDOM)
|
|
||||||
check_function_exists(setmode HAVE_SETMODE)
|
|
||||||
check_symbol_exists(snprintf stdio.h HAVE_SNPRINTF)
|
|
||||||
check_symbol_exists(snprintf_s stdio.h HAVE_SNPRINTF_S)
|
|
||||||
check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP)
|
|
||||||
check_function_exists(strdup HAVE_STRDUP)
|
|
||||||
check_function_exists(strerror_s HAVE_STRERROR_S)
|
|
||||||
check_function_exists(strerrorlen_s HAVE_STRERRORLEN_S)
|
|
||||||
check_function_exists(stricmp HAVE_STRICMP)
|
|
||||||
check_function_exists(strncpy_s HAVE_STRNCPY_S)
|
|
||||||
check_function_exists(strtoll HAVE_STRTOLL)
|
|
||||||
check_function_exists(strtoull HAVE_STRTOULL)
|
|
||||||
|
|
||||||
check_include_files("sys/types.h;sys/stat.h;fts.h" HAVE_FTS_H)
|
|
||||||
# fts functions may be in external library
|
|
||||||
if(HAVE_FTS_H)
|
|
||||||
check_function_exists(fts_open HAVE_FTS_OPEN)
|
|
||||||
if(NOT HAVE_FTS_OPEN)
|
|
||||||
check_library_exists(fts fts_open "" HAVE_LIB_FTS)
|
|
||||||
else(NOT HAVE_FTS_OPEN)
|
|
||||||
set(HAVE_LIB_FTS "" CACHE INTERNAL "")
|
|
||||||
endif(NOT HAVE_FTS_OPEN)
|
|
||||||
else(HAVE_FTS_H)
|
|
||||||
set(HAVE_LIB_FTS "" CACHE INTERNAL "")
|
|
||||||
endif(HAVE_FTS_H)
|
|
||||||
|
|
||||||
if(HAVE_LIB_FTS)
|
|
||||||
set(FTS_LIB fts CACHE INTERNAL "")
|
|
||||||
else()
|
|
||||||
set(FTS_LIB "" CACHE INTERNAL "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(stdbool.h HAVE_STDBOOL_H)
|
|
||||||
check_include_files(strings.h HAVE_STRINGS_H)
|
|
||||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
|
||||||
|
|
||||||
check_include_files(inttypes.h HAVE_INTTYPES_H_LIBZIP)
|
|
||||||
check_include_files(stdint.h HAVE_STDINT_H_LIBZIP)
|
|
||||||
check_include_files(sys/types.h HAVE_SYS_TYPES_H_LIBZIP)
|
|
||||||
|
|
||||||
# TODO: fix test
|
|
||||||
# this test does not find __progname even when it exists
|
|
||||||
#check_symbol_exists(__progname stdlib.h HAVE___PROGNAME)
|
|
||||||
|
|
||||||
check_type_size(__int8 __INT8_LIBZIP)
|
|
||||||
check_type_size(int8_t INT8_T_LIBZIP)
|
|
||||||
check_type_size(uint8_t UINT8_T_LIBZIP)
|
|
||||||
check_type_size(__int16 __INT16_LIBZIP)
|
|
||||||
check_type_size(int16_t INT16_T_LIBZIP)
|
|
||||||
check_type_size(uint16_t UINT16_T_LIBZIP)
|
|
||||||
check_type_size(__int32 __INT32_LIBZIP)
|
|
||||||
check_type_size(int32_t INT32_T_LIBZIP)
|
|
||||||
check_type_size(uint32_t UINT32_T_LIBZIP)
|
|
||||||
check_type_size(__int64 __INT64_LIBZIP)
|
|
||||||
check_type_size(int64_t INT64_T_LIBZIP)
|
|
||||||
check_type_size(uint64_t UINT64_T_LIBZIP)
|
|
||||||
check_type_size("short" SHORT_LIBZIP)
|
|
||||||
check_type_size("int" INT_LIBZIP)
|
|
||||||
check_type_size("long" LONG_LIBZIP)
|
|
||||||
check_type_size("long long" LONG_LONG_LIBZIP)
|
|
||||||
check_type_size("off_t" SIZEOF_OFF_T)
|
|
||||||
check_type_size("size_t" SIZEOF_SIZE_T)
|
|
||||||
|
|
||||||
check_c_source_compiles("#include <sys/ioctl.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
int main(int argc, char *argv[]) { unsigned long x = FICLONERANGE; }" HAVE_FICLONERANGE)
|
|
||||||
|
|
||||||
test_big_endian(WORDS_BIGENDIAN)
|
|
||||||
|
|
||||||
find_package(ZLIB 1.1.2 REQUIRED)
|
|
||||||
# so developers on systems where zlib is named differently (Windows, sometimes)
|
|
||||||
# can override the name used in the pkg-config file
|
|
||||||
if (NOT ZLIB_LINK_LIBRARY_NAME)
|
|
||||||
set(ZLIB_LINK_LIBRARY_NAME "z")
|
|
||||||
|
|
||||||
# Get the correct name in common cases
|
|
||||||
list(LENGTH ZLIB_LIBRARIES N_ZLIB_LIBRARIES)
|
|
||||||
if(N_ZLIB_LIBRARIES EQUAL 1)
|
|
||||||
set(ZLIB_FILENAME ${ZLIB_LIBRARIES})
|
|
||||||
elseif(N_ZLIB_LIBRARIES EQUAL 4)
|
|
||||||
# ZLIB_LIBRARIES might have the target_link_library() format like
|
|
||||||
# "optimized;path/to/zlib.lib;debug;path/to/zlibd.lib". Use the 'optimized'
|
|
||||||
# case unless we know we are in a Debug build.
|
|
||||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|
||||||
list(FIND ZLIB_LIBRARIES "debug" ZLIB_LIBRARIES_INDEX_OF_CONFIG)
|
|
||||||
else()
|
|
||||||
list(FIND ZLIB_LIBRARIES "optimized" ZLIB_LIBRARIES_INDEX_OF_CONFIG)
|
|
||||||
endif()
|
|
||||||
if(ZLIB_LIBRARIES_INDEX_OF_CONFIG GREATER_EQUAL 0)
|
|
||||||
math(EXPR ZLIB_FILENAME_INDEX "${ZLIB_LIBRARIES_INDEX_OF_CONFIG}+1")
|
|
||||||
list(GET ZLIB_LIBRARIES ${ZLIB_FILENAME_INDEX} ZLIB_FILENAME)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(ZLIB_FILENAME)
|
|
||||||
get_filename_component(ZLIB_FILENAME ${ZLIB_FILENAME} NAME_WE)
|
|
||||||
string(REGEX REPLACE "^lib" "" ZLIB_LINK_LIBRARY_NAME ${ZLIB_FILENAME})
|
|
||||||
endif()
|
|
||||||
endif(NOT ZLIB_LINK_LIBRARY_NAME)
|
|
||||||
|
|
||||||
if(ENABLE_BZIP2)
|
|
||||||
find_package(BZip2)
|
|
||||||
if(BZIP2_FOUND)
|
|
||||||
set(HAVE_LIBBZ2 1)
|
|
||||||
else()
|
|
||||||
message(WARNING "-- bzip2 library not found; bzip2 support disabled")
|
|
||||||
endif(BZIP2_FOUND)
|
|
||||||
endif(ENABLE_BZIP2)
|
|
||||||
|
|
||||||
if(ENABLE_LZMA)
|
|
||||||
find_package(LibLZMA 5.2)
|
|
||||||
if(LIBLZMA_FOUND)
|
|
||||||
set(HAVE_LIBLZMA 1)
|
|
||||||
else()
|
|
||||||
message(WARNING "-- lzma library not found; lzma/xz support disabled")
|
|
||||||
endif(LIBLZMA_FOUND)
|
|
||||||
endif(ENABLE_LZMA)
|
|
||||||
|
|
||||||
if(ENABLE_ZSTD)
|
|
||||||
find_package(zstd 1.4.0)
|
|
||||||
if(zstd_FOUND)
|
|
||||||
set(HAVE_LIBZSTD 1)
|
|
||||||
if(TARGET zstd::libzstd_shared AND BUILD_SHARED_LIBS)
|
|
||||||
set(zstd_TARGET zstd::libzstd_shared)
|
|
||||||
else()
|
|
||||||
set(zstd_TARGET zstd::libzstd_static)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(WARNING "-- zstd library not found; zstandard support disabled")
|
|
||||||
endif(zstd_FOUND)
|
|
||||||
endif(ENABLE_ZSTD)
|
|
||||||
|
|
||||||
if (COMMONCRYPTO_FOUND)
|
|
||||||
set(HAVE_CRYPTO 1)
|
|
||||||
set(HAVE_COMMONCRYPTO 1)
|
|
||||||
elseif (WINDOWS_CRYPTO_FOUND)
|
|
||||||
set(HAVE_CRYPTO 1)
|
|
||||||
set(HAVE_WINDOWS_CRYPTO 1)
|
|
||||||
elseif (OPENSSL_FOUND)
|
|
||||||
set(HAVE_CRYPTO 1)
|
|
||||||
set(HAVE_OPENSSL 1)
|
|
||||||
elseif (GNUTLS_FOUND AND NETTLE_FOUND)
|
|
||||||
set(HAVE_CRYPTO 1)
|
|
||||||
set(HAVE_GNUTLS 1)
|
|
||||||
elseif (MBEDTLS_FOUND)
|
|
||||||
set(HAVE_CRYPTO 1)
|
|
||||||
set(HAVE_MBEDTLS 1)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if ((ENABLE_COMMONCRYPTO OR ENABLE_GNUTLS OR ENABLE_MBEDTLS OR ENABLE_OPENSSL OR ENABLE_WINDOWS_CRYPTO) AND NOT HAVE_CRYPTO)
|
|
||||||
message(WARNING "-- neither Common Crypto, GnuTLS, mbed TLS, OpenSSL, nor Windows Cryptography found; AES support disabled")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
|
||||||
add_compile_definitions(_CRT_NONSTDC_NO_DEPRECATE)
|
|
||||||
endif(MSVC)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
|
||||||
add_compile_definitions(MS_UWP)
|
|
||||||
endif(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore)
|
|
||||||
endif(WIN32)
|
|
||||||
|
|
||||||
# rpath handling: use rpath in installed binaries
|
|
||||||
if(NOT CMAKE_SYSTEM_NAME MATCHES Linux)
|
|
||||||
set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR})
|
|
||||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# for code completion frameworks
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
ENABLE_TESTING()
|
|
||||||
|
|
||||||
# Targets
|
|
||||||
ADD_SUBDIRECTORY(lib)
|
|
||||||
|
|
||||||
if(BUILD_DOC)
|
|
||||||
ADD_SUBDIRECTORY(man)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_TOOLS)
|
|
||||||
ADD_SUBDIRECTORY(src)
|
|
||||||
else(BUILD_TOOLS)
|
|
||||||
if(BUILD_REGRESS)
|
|
||||||
message(WARNING "-- tools build has been disabled, but they are needed for regression tests; regression testing disabled")
|
|
||||||
set(BUILD_REGRESS OFF)
|
|
||||||
endif(BUILD_REGRESS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_program(NIHTEST nihtest)
|
|
||||||
|
|
||||||
if(BUILD_REGRESS AND NOT NIHTEST)
|
|
||||||
message(WARNING "-- nihtest not found, regression testing disabled")
|
|
||||||
set(BUILD_REGRESS OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_REGRESS)
|
|
||||||
add_subdirectory(regress)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_OSSFUZZ)
|
|
||||||
add_subdirectory(ossfuzz)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_EXAMPLES)
|
|
||||||
add_subdirectory(examples)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# pkgconfig file
|
|
||||||
file(RELATIVE_PATH pc_relative_bindir ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_FULL_BINDIR})
|
|
||||||
set(bindir "\${prefix}/${pc_relative_bindir}")
|
|
||||||
file(RELATIVE_PATH pc_relative_libdir ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_FULL_LIBDIR})
|
|
||||||
set(libdir "\${prefix}/${pc_relative_libdir}")
|
|
||||||
file(RELATIVE_PATH pc_relative_includedir ${CMAKE_INSTALL_PREFIX} ${CMAKE_INSTALL_FULL_INCLUDEDIR})
|
|
||||||
set(includedir "\${prefix}/${pc_relative_includedir}")
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES BSD)
|
|
||||||
set(PKG_CONFIG_RPATH "-Wl,-R\${libdir}")
|
|
||||||
endif(CMAKE_SYSTEM_NAME MATCHES BSD)
|
|
||||||
get_target_property(LIBS_PRIVATE zip LINK_LIBRARIES)
|
|
||||||
foreach(LIB ${LIBS_PRIVATE})
|
|
||||||
if(LIB MATCHES "^/")
|
|
||||||
get_filename_component(LIB ${LIB} NAME_WE)
|
|
||||||
string(REGEX REPLACE "^lib" "" LIB ${LIB})
|
|
||||||
endif()
|
|
||||||
set(LIBS "${LIBS} -l${LIB}")
|
|
||||||
endforeach()
|
|
||||||
STRING(CONCAT zlib_link_name "-l" ${ZLIB_LINK_LIBRARY_NAME})
|
|
||||||
string(REGEX REPLACE "-lBZip2::BZip2" "-lbz2" LIBS ${LIBS})
|
|
||||||
string(REGEX REPLACE "-lLibLZMA::LibLZMA" "-llzma" LIBS ${LIBS})
|
|
||||||
if(zstd_TARGET)
|
|
||||||
string(REGEX REPLACE "-l${zstd_TARGET}" "-lzstd" LIBS ${LIBS})
|
|
||||||
endif()
|
|
||||||
string(REGEX REPLACE "-lOpenSSL::Crypto" "-lssl -lcrypto" LIBS ${LIBS})
|
|
||||||
string(REGEX REPLACE "-lZLIB::ZLIB" ${zlib_link_name} LIBS ${LIBS})
|
|
||||||
string(REGEX REPLACE "-lGnuTLS::GnuTLS" "-lgnutls" LIBS ${LIBS})
|
|
||||||
string(REGEX REPLACE "-lNettle::Nettle" "-lnettle" LIBS ${LIBS})
|
|
||||||
configure_file(libzip.pc.in libzip.pc @ONLY)
|
|
||||||
if(LIBZIP_DO_INSTALL)
|
|
||||||
install(FILES ${PROJECT_BINARY_DIR}/libzip.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# fixed size integral types
|
|
||||||
|
|
||||||
if(HAVE_INTTYPES_H_LIBZIP)
|
|
||||||
set(LIBZIP_TYPES_INCLUDE "#if !defined(__STDC_FORMAT_MACROS)
|
|
||||||
#define __STDC_FORMAT_MACROS 1
|
|
||||||
#endif
|
|
||||||
#include <inttypes.h>")
|
|
||||||
elseif(HAVE_STDINT_H_LIBZIP)
|
|
||||||
set(LIBZIP_TYPES_INCLUDE "#include <stdint.h>")
|
|
||||||
elseif(HAVE_SYS_TYPES_H_LIBZIP)
|
|
||||||
set(LIBZIP_TYPES_INCLUDE "#include <sys/types.h>")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_INT8_T_LIBZIP)
|
|
||||||
set(ZIP_INT8_T int8_t)
|
|
||||||
elseif(HAVE___INT8_LIBZIP)
|
|
||||||
set(ZIP_INT8_T __int8)
|
|
||||||
else()
|
|
||||||
set(ZIP_INT8_T "signed char")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_UINT8_T_LIBZIP)
|
|
||||||
set(ZIP_UINT8_T uint8_t)
|
|
||||||
elseif(HAVE___INT8_LIBZIP)
|
|
||||||
set(ZIP_UINT8_T "unsigned __int8")
|
|
||||||
else()
|
|
||||||
set(ZIP_UINT8_T "unsigned char")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_INT16_T_LIBZIP)
|
|
||||||
set(ZIP_INT16_T int16_t)
|
|
||||||
elseif(HAVE___INT16_LIBZIP)
|
|
||||||
set(INT16_T_LIBZIP __int16)
|
|
||||||
elseif(SHORT_LIBZIP EQUAL 2)
|
|
||||||
set(INT16_T_LIBZIP short)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_UINT16_T_LIBZIP)
|
|
||||||
set(ZIP_UINT16_T uint16_t)
|
|
||||||
elseif(HAVE___INT16_LIBZIP)
|
|
||||||
set(UINT16_T_LIBZIP "unsigned __int16")
|
|
||||||
elseif(SHORT_LIBZIP EQUAL 2)
|
|
||||||
set(UINT16_T_LIBZIP "unsigned short")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_INT32_T_LIBZIP)
|
|
||||||
set(ZIP_INT32_T int32_t)
|
|
||||||
elseif(HAVE___INT32_LIBZIP)
|
|
||||||
set(ZIP_INT32_T __int32)
|
|
||||||
elseif(INT_LIBZIP EQUAL 4)
|
|
||||||
set(ZIP_INT32_T int)
|
|
||||||
elseif(LONG_LIBZIP EQUAL 4)
|
|
||||||
set(ZIP_INT32_T long)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_UINT32_T_LIBZIP)
|
|
||||||
set(ZIP_UINT32_T uint32_t)
|
|
||||||
elseif(HAVE___INT32_LIBZIP)
|
|
||||||
set(ZIP_UINT32_T "unsigned __int32")
|
|
||||||
elseif(INT_LIBZIP EQUAL 4)
|
|
||||||
set(ZIP_UINT32_T "unsigned int")
|
|
||||||
elseif(LONG_LIBZIP EQUAL 4)
|
|
||||||
set(ZIP_UINT32_T "unsigned long")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_INT64_T_LIBZIP)
|
|
||||||
set(ZIP_INT64_T int64_t)
|
|
||||||
elseif(HAVE___INT64_LIBZIP)
|
|
||||||
set(ZIP_INT64_T __int64)
|
|
||||||
elseif(LONG_LIBZIP EQUAL 8)
|
|
||||||
set(ZIP_INT64_T long)
|
|
||||||
elseif(LONG_LONG_LIBZIP EQUAL 8)
|
|
||||||
set(ZIP_INT64_T "long long")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(HAVE_UINT64_T_LIBZIP)
|
|
||||||
set(ZIP_UINT64_T uint64_t)
|
|
||||||
elseif(HAVE___INT64_LIBZIP)
|
|
||||||
set(ZIP_UINT64_T "unsigned __int64")
|
|
||||||
elseif(LONG_LIBZIP EQUAL 8)
|
|
||||||
set(ZIP_UINT64_T "unsigned long")
|
|
||||||
elseif(LONG_LONG_LIBZIP EQUAL 8)
|
|
||||||
set(ZIP_UINT64_T "unsigned long long")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# write out config file
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h)
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/zipconf.h.in ${PROJECT_BINARY_DIR}/zipconf.h)
|
|
||||||
|
|
||||||
# for tests
|
|
||||||
|
|
||||||
set(srcdir ${CMAKE_CURRENT_SOURCE_DIR}/regress)
|
|
||||||
set(abs_srcdir ${CMAKE_CURRENT_SOURCE_DIR}/regress)
|
|
||||||
set(top_builddir ${PROJECT_BINARY_DIR}) # used to find config.h
|
|
||||||
|
|
||||||
# create package config file
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
write_basic_package_version_file("${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake"
|
|
||||||
COMPATIBILITY AnyNewerVersion)
|
|
||||||
|
|
||||||
if(LIBZIP_DO_INSTALL)
|
|
||||||
configure_package_config_file("${PROJECT_NAME}-config.cmake.in" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
|
|
||||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libzip)
|
|
||||||
|
|
||||||
# Install Find* modules, they are required by libzip-config.cmake to resolve dependencies
|
|
||||||
install(FILES
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindNettle.cmake
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findzstd.cmake
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindMbedTLS.cmake
|
|
||||||
DESTINATION
|
|
||||||
${CMAKE_INSTALL_LIBDIR}/cmake/libzip/modules
|
|
||||||
)
|
|
||||||
|
|
||||||
# Add targets to the build-tree export set
|
|
||||||
export(TARGETS zip
|
|
||||||
FILE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-targets.cmake")
|
|
||||||
|
|
||||||
# installation
|
|
||||||
install(FILES ${PROJECT_BINARY_DIR}/zipconf.h DESTINATION include)
|
|
||||||
install(FILES ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
|
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
|
|
||||||
)
|
|
||||||
install(EXPORT ${PROJECT_NAME}-targets NAMESPACE libzip:: FILE ${PROJECT_NAME}-targets.cmake
|
|
||||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
Vendored
-71
@@ -1,71 +0,0 @@
|
|||||||
libzip uses [cmake](https://cmake.org) to build.
|
|
||||||
|
|
||||||
You'll need [zlib](http://www.zlib.net/) (at least version 1.1.2). It
|
|
||||||
comes with most operating systems.
|
|
||||||
|
|
||||||
For supporting bzip2-compressed zip archives, you need
|
|
||||||
[bzip2](http://bzip.org/).
|
|
||||||
|
|
||||||
For supporting lzma- and xz-compressed zip archives, you need
|
|
||||||
[liblzma](https://tukaani.org/xz/) which is part of xz, at least version 5.2.
|
|
||||||
|
|
||||||
For supporting zstd-compressed zip archives, you need
|
|
||||||
[zstd](https://github.com/facebook/zstd/).
|
|
||||||
|
|
||||||
For AES (encryption) support, you need one of these cryptographic libraries,
|
|
||||||
listed in order of preference:
|
|
||||||
|
|
||||||
- Apple's CommonCrypto (available on macOS and iOS)
|
|
||||||
- Microsoft Windows Cryptography Framework
|
|
||||||
- [OpenSSL](https://www.openssl.org/) >= 1.0.
|
|
||||||
- [GnuTLS](https://www.gnutls.org/) and [Nettle](https://www.lysator.liu.se/~nisse/nettle/) (at least nettle 3.0)
|
|
||||||
- [mbed TLS](https://tls.mbed.org/)
|
|
||||||
|
|
||||||
If you don't want a library even if it is installed, you can
|
|
||||||
pass `-DENABLE_<LIBRARY>=OFF` to cmake, where `<LIBRARY>` is one of
|
|
||||||
`COMMONCRYPTO`, `GNUTLS`, `MBEDTLS`, or `OPENSSL`.
|
|
||||||
|
|
||||||
For running the tests, you need to have
|
|
||||||
[Python](https://www.python.org/) and
|
|
||||||
[nihtest](https://pypi.org/project/nihtest/) installed.
|
|
||||||
|
|
||||||
The basic usage is
|
|
||||||
```sh
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make
|
|
||||||
make test
|
|
||||||
make install
|
|
||||||
```
|
|
||||||
|
|
||||||
Some useful parameters you can pass to `cmake` with `-Dparameter=value`:
|
|
||||||
|
|
||||||
- `BUILD_SHARED_LIBS`: set to `ON` or `OFF` to enable/disable building
|
|
||||||
of shared libraries, defaults to `ON`
|
|
||||||
- `CMAKE_INSTALL_PREFIX`: for setting the installation path
|
|
||||||
- `DOCUMENTATION_FORMAT`: choose one of `man`, `mdoc`, and `html` for
|
|
||||||
the installed documentation (default: decided by cmake depending on
|
|
||||||
available tools)
|
|
||||||
- `LIBZIP_DO_INSTALL`: If you include libzip as a subproject, link it
|
|
||||||
statically and do not want to let it install its files, set this
|
|
||||||
variable to `OFF`. Defaults to `ON`.
|
|
||||||
|
|
||||||
If you want to compile with custom `CFLAGS`, set them in the environment
|
|
||||||
before running `cmake`:
|
|
||||||
```sh
|
|
||||||
CFLAGS=-DMY_CUSTOM_FLAG cmake ..
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are compiling on a system with a small stack size, add
|
|
||||||
`-DZIP_ALLOCATE_BUFFER` to `CFLAGS`.
|
|
||||||
|
|
||||||
If you are building on a 32-bit Linux system it might be necessary
|
|
||||||
to define `_FILE_OFFSET_BITS` to `64`. Your distro will need to provide
|
|
||||||
a `fts.h` file that is new enough to support this, or the build
|
|
||||||
will break in `zipcmp`.
|
|
||||||
|
|
||||||
You can get verbose build output with by passing `VERBOSE=1` to
|
|
||||||
`make`.
|
|
||||||
|
|
||||||
You can also check the [cmake FAQ](https://gitlab.kitware.com/cmake/community/-/wikis/FAQ).
|
|
||||||
Vendored
-31
@@ -1,31 +0,0 @@
|
|||||||
Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner
|
|
||||||
|
|
||||||
The authors can be contacted at <info@libzip.org>
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in
|
|
||||||
the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
|
|
||||||
3. The names of the authors may not be used to endorse or promote
|
|
||||||
products derived from this software without specific prior
|
|
||||||
written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
|
|
||||||
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
|
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
||||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
||||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
||||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
||||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
Loaded 100 of 2138 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user