Compare commits
76 Commits
temp-colle
...
soc-2021-c
Author | SHA1 | Date | |
---|---|---|---|
![]() |
162a3d263d | ||
![]() |
0ed4945aab | ||
![]() |
22471e847e | ||
![]() |
73371635ba | ||
![]() |
544519b49e | ||
![]() |
a88037c64d | ||
![]() |
d216ac4d21 | ||
![]() |
cbdda7c82f | ||
![]() |
2312ef78f7 | ||
![]() |
de1a6b0ca8 | ||
![]() |
d71aaf0d50 | ||
![]() |
3b1477c27d | ||
![]() |
9d2e9c5142 | ||
![]() |
2a7b2bf41b | ||
![]() |
625fe5968f | ||
![]() |
b644728ac6 | ||
![]() |
e9f1cc6881 | ||
![]() |
f5785921eb | ||
![]() |
5aa5a349d8 | ||
![]() |
c826522669 | ||
![]() |
c07be1e484 | ||
![]() |
ca91b7f526 | ||
![]() |
94ef272b43 | ||
![]() |
9b1af3d9b5 | ||
![]() |
d9d7c5c028 | ||
![]() |
6ab7bb9483 | ||
![]() |
fbfcbc740e | ||
![]() |
14e8e8826e | ||
![]() |
381babb958 | ||
![]() |
f15b3c0f10 | ||
![]() |
b662173428 | ||
![]() |
658e59bb0b | ||
![]() |
e515553bd3 | ||
![]() |
dce4331af7 | ||
![]() |
2c4da9b27e | ||
![]() |
e1058b69fa | ||
![]() |
36c3958a50 | ||
![]() |
42d0360d92 | ||
![]() |
772a053ee8 | ||
![]() |
539fb9d04a | ||
![]() |
b658b53984 | ||
![]() |
a52c51e3df | ||
![]() |
242814f34d | ||
![]() |
e4d3a5518c | ||
![]() |
caa88b3087 | ||
![]() |
dea495fd3a | ||
![]() |
38c199ab3e | ||
![]() |
9212e59ffe | ||
![]() |
ef6db37b1f | ||
![]() |
320d158c31 | ||
![]() |
2aca184623 | ||
![]() |
8615da0e77 | ||
![]() |
16b65ce31a | ||
![]() |
a9488cead9 | ||
![]() |
4511fc6265 | ||
![]() |
49048ce946 | ||
![]() |
c91f1db9c6 | ||
![]() |
cf5d9cf2fc | ||
![]() |
bb8bdd55c2 | ||
![]() |
44288a4538 | ||
![]() |
58a3aafa26 | ||
![]() |
b4a358b014 | ||
![]() |
73a58be3fa | ||
![]() |
cd79bf179d | ||
![]() |
2323267ed3 | ||
![]() |
8141d4a835 | ||
![]() |
76750c3ee2 | ||
![]() |
165a390dc4 | ||
![]() |
69993d08f9 | ||
![]() |
6808e774a3 | ||
![]() |
8d7a1615b7 | ||
![]() |
012aa1c472 | ||
![]() |
80c7a228fa | ||
![]() |
1d4c5d25c3 | ||
![]() |
3b3a08f439 | ||
![]() |
b4de48d3b2 |
@@ -14,7 +14,7 @@ sound = aud.Sound('music.ogg')
|
||||
# play the audio, this return a handle to control play/pause
|
||||
handle = device.play(sound)
|
||||
# if the audio is not too big and will be used often you can buffer it
|
||||
sound_buffered = aud.Sound.buffer(sound)
|
||||
sound_buffered = aud.Sound.cache(sound)
|
||||
handle_buffered = device.play(sound_buffered)
|
||||
|
||||
# stop the sounds (otherwise they play until their ends)
|
||||
|
@@ -10,4 +10,4 @@ requests==2.26.0
|
||||
|
||||
# Only needed to match the theme used for the official documentation.
|
||||
# Without this theme, the default theme will be used.
|
||||
sphinx_rtd_theme==1.0.0rc1
|
||||
sphinx_rtd_theme==1.0.0
|
||||
|
6
extern/audaspace/CMakeLists.txt
vendored
6
extern/audaspace/CMakeLists.txt
vendored
@@ -152,6 +152,7 @@ set(PUBLIC_HDR
|
||||
include/devices/ThreadedDevice.h
|
||||
include/Exception.h
|
||||
include/file/File.h
|
||||
include/file/FileInfo.h
|
||||
include/file/FileManager.h
|
||||
include/file/FileWriter.h
|
||||
include/file/IFileInput.h
|
||||
@@ -960,7 +961,10 @@ endif()
|
||||
if(BUILD_DEMOS)
|
||||
include_directories(${INCLUDE})
|
||||
|
||||
set(DEMOS audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager)
|
||||
set(DEMOS audainfo audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager)
|
||||
|
||||
add_executable(audainfo demos/audainfo.cpp)
|
||||
target_link_libraries(audainfo audaspace)
|
||||
|
||||
add_executable(audaplay demos/audaplay.cpp)
|
||||
target_link_libraries(audaplay audaspace)
|
||||
|
@@ -39,7 +39,7 @@ extern AUD_API void AUD_PlaybackManager_free(AUD_PlaybackManager* manager);
|
||||
* Plays a sound through the playback manager, adding it into a category.
|
||||
* \param manager The PlaybackManager object.
|
||||
* \param sound The sound to be played.
|
||||
* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be creatd.
|
||||
* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be created.
|
||||
*/
|
||||
extern AUD_API void AUD_PlaybackManager_play(AUD_PlaybackManager* manager, AUD_Sound* sound, unsigned int catKey);
|
||||
|
||||
|
42
extern/audaspace/bindings/C/AUD_Sound.cpp
vendored
42
extern/audaspace/bindings/C/AUD_Sound.cpp
vendored
@@ -94,6 +94,36 @@ AUD_API int AUD_Sound_getLength(AUD_Sound* sound)
|
||||
return (*sound)->createReader()->getLength();
|
||||
}
|
||||
|
||||
AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo **stream_infos)
|
||||
{
|
||||
assert(sound);
|
||||
|
||||
std::shared_ptr<File> file = std::dynamic_pointer_cast<File>(*sound);
|
||||
|
||||
if(file)
|
||||
{
|
||||
auto streams = file->queryStreams();
|
||||
|
||||
size_t size = sizeof(AUD_StreamInfo) * streams.size();
|
||||
|
||||
if(!size)
|
||||
{
|
||||
*stream_infos = nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*stream_infos = reinterpret_cast<AUD_StreamInfo*>(std::malloc(size));
|
||||
std::memcpy(*stream_infos, streams.data(), size);
|
||||
|
||||
return streams.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
*stream_infos = nullptr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs)
|
||||
{
|
||||
assert(sound);
|
||||
@@ -252,6 +282,12 @@ AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size)
|
||||
return new AUD_Sound(new File(buffer, size));
|
||||
}
|
||||
|
||||
AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream)
|
||||
{
|
||||
assert(buffer);
|
||||
return new AUD_Sound(new File(buffer, size, stream));
|
||||
}
|
||||
|
||||
AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound)
|
||||
{
|
||||
assert(sound);
|
||||
@@ -272,6 +308,12 @@ AUD_API AUD_Sound* AUD_Sound_file(const char* filename)
|
||||
return new AUD_Sound(new File(filename));
|
||||
}
|
||||
|
||||
AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream)
|
||||
{
|
||||
assert(filename);
|
||||
return new AUD_Sound(new File(filename, stream));
|
||||
}
|
||||
|
||||
AUD_API AUD_Sound* AUD_Sound_sawtooth(float frequency, AUD_SampleRate rate)
|
||||
{
|
||||
return new AUD_Sound(new Sawtooth(frequency, rate));
|
||||
|
27
extern/audaspace/bindings/C/AUD_Sound.h
vendored
27
extern/audaspace/bindings/C/AUD_Sound.h
vendored
@@ -36,7 +36,15 @@ extern AUD_API AUD_Specs AUD_Sound_getSpecs(AUD_Sound* sound);
|
||||
* \return The length of the sound in samples.
|
||||
* \note This function creates a reader from the sound and deletes it again.
|
||||
*/
|
||||
extern AUD_API int AUD_getLength(AUD_Sound* sound);
|
||||
extern AUD_API int AUD_Sound_getLength(AUD_Sound* sound);
|
||||
|
||||
/**
|
||||
* Retrieves the stream infos of a sound file.
|
||||
* \param sound The sound to retrieve from which must be a file sound.
|
||||
* \param infos A pointer to a AUD_StreamInfo array that will be allocated and must afterwards be freed by the caller.
|
||||
* \return The number of items in the infos array.
|
||||
*/
|
||||
extern AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo** stream_infos);
|
||||
|
||||
/**
|
||||
* Reads a sound's samples into memory.
|
||||
@@ -89,6 +97,15 @@ extern AUD_API AUD_Sound* AUD_Sound_buffer(sample_t* data, int length, AUD_Specs
|
||||
*/
|
||||
extern AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size);
|
||||
|
||||
/**
|
||||
* Loads a sound file from a memory buffer.
|
||||
* \param buffer The buffer which contains the sound file.
|
||||
* \param size The size of the buffer.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \return A handle of the sound file.
|
||||
*/
|
||||
extern AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream);
|
||||
|
||||
/**
|
||||
* Caches a sound into a memory buffer.
|
||||
* \param sound The sound to cache.
|
||||
@@ -103,6 +120,14 @@ extern AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound);
|
||||
*/
|
||||
extern AUD_API AUD_Sound* AUD_Sound_file(const char* filename);
|
||||
|
||||
/**
|
||||
* Loads a sound file.
|
||||
* \param filename The filename of the sound file.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \return A handle of the sound file.
|
||||
*/
|
||||
extern AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream);
|
||||
|
||||
/**
|
||||
* Creates a sawtooth sound.
|
||||
* \param frequency The frequency of the generated sawtooth sound.
|
||||
|
8
extern/audaspace/bindings/C/AUD_Special.cpp
vendored
8
extern/audaspace/bindings/C/AUD_Special.cpp
vendored
@@ -86,7 +86,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
|
||||
info.specs.channels = AUD_CHANNELS_INVALID;
|
||||
info.specs.rate = AUD_RATE_INVALID;
|
||||
info.length = 0.0f;
|
||||
info.start_offset = 0.0f;
|
||||
|
||||
try
|
||||
{
|
||||
@@ -96,7 +95,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
|
||||
{
|
||||
info.specs = convSpecToC(reader->getSpecs());
|
||||
info.length = reader->getLength() / (float) info.specs.rate;
|
||||
info.start_offset = reader->getStartOffset();
|
||||
}
|
||||
}
|
||||
catch(Exception&)
|
||||
@@ -109,7 +107,7 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
|
||||
AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high,
|
||||
float attack, float release, float threshold,
|
||||
int accumulate, int additive, int square,
|
||||
float sthreshold, double samplerate, int* length)
|
||||
float sthreshold, double samplerate, int* length, int stream)
|
||||
{
|
||||
Buffer buffer;
|
||||
DeviceSpecs specs;
|
||||
@@ -117,7 +115,7 @@ AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high,
|
||||
specs.rate = (SampleRate)samplerate;
|
||||
std::shared_ptr<ISound> sound;
|
||||
|
||||
std::shared_ptr<ISound> file = std::shared_ptr<ISound>(new File(filename));
|
||||
std::shared_ptr<ISound> file = std::shared_ptr<ISound>(new File(filename, stream));
|
||||
|
||||
int position = 0;
|
||||
|
||||
@@ -247,7 +245,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl
|
||||
|
||||
buffer[i * 3] = min;
|
||||
buffer[i * 3 + 1] = max;
|
||||
buffer[i * 3 + 2] = sqrt(power / len); // RMS
|
||||
buffer[i * 3 + 2] = std::sqrt(power / len);
|
||||
|
||||
if(overallmax < max)
|
||||
overallmax = max;
|
||||
|
2
extern/audaspace/bindings/C/AUD_Special.h
vendored
2
extern/audaspace/bindings/C/AUD_Special.h
vendored
@@ -37,7 +37,7 @@ extern AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float
|
||||
float attack, float release, float threshold,
|
||||
int accumulate, int additive, int square,
|
||||
float sthreshold, double samplerate,
|
||||
int* length);
|
||||
int* length, int stream);
|
||||
|
||||
/**
|
||||
* Pauses a playing sound after a specific amount of time.
|
||||
|
14
extern/audaspace/bindings/C/AUD_Types.h
vendored
14
extern/audaspace/bindings/C/AUD_Types.h
vendored
@@ -176,5 +176,17 @@ typedef struct
|
||||
{
|
||||
AUD_Specs specs;
|
||||
float length;
|
||||
double start_offset;
|
||||
} AUD_SoundInfo;
|
||||
|
||||
/// Specification of a sound source.
|
||||
typedef struct
|
||||
{
|
||||
/// Start time in seconds.
|
||||
double start;
|
||||
|
||||
/// Duration in seconds. May be estimated or 0 if unknown.
|
||||
double duration;
|
||||
|
||||
/// Audio data parameters.
|
||||
AUD_DeviceSpecs specs;
|
||||
} AUD_StreamInfo;
|
||||
|
12
extern/audaspace/bindings/python/PySound.cpp
vendored
12
extern/audaspace/bindings/python/PySound.cpp
vendored
@@ -89,10 +89,11 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
|
||||
self = (Sound*)type->tp_alloc(type, 0);
|
||||
if(self != nullptr)
|
||||
{
|
||||
static const char* kwlist[] = {"filename", nullptr};
|
||||
static const char* kwlist[] = {"filename", "stream", nullptr};
|
||||
const char* filename = nullptr;
|
||||
int stream = 0;
|
||||
|
||||
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s:Sound", const_cast<char**>(kwlist), &filename))
|
||||
if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|i:Sound", const_cast<char**>(kwlist), &filename, &stream))
|
||||
{
|
||||
Py_DECREF(self);
|
||||
return nullptr;
|
||||
@@ -100,7 +101,7 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
|
||||
|
||||
try
|
||||
{
|
||||
self->sound = new std::shared_ptr<ISound>(new File(filename));
|
||||
self->sound = new std::shared_ptr<ISound>(new File(filename, stream));
|
||||
}
|
||||
catch(Exception& e)
|
||||
{
|
||||
@@ -407,8 +408,9 @@ static PyObject *
|
||||
Sound_file(PyTypeObject* type, PyObject* args)
|
||||
{
|
||||
const char* filename = nullptr;
|
||||
int stream = 0;
|
||||
|
||||
if(!PyArg_ParseTuple(args, "s:file", &filename))
|
||||
if(!PyArg_ParseTuple(args, "s|i:file", &filename, &stream))
|
||||
return nullptr;
|
||||
|
||||
Sound* self;
|
||||
@@ -418,7 +420,7 @@ Sound_file(PyTypeObject* type, PyObject* args)
|
||||
{
|
||||
try
|
||||
{
|
||||
self->sound = new std::shared_ptr<ISound>(new File(filename));
|
||||
self->sound = new std::shared_ptr<ISound>(new File(filename, stream));
|
||||
}
|
||||
catch(Exception& e)
|
||||
{
|
||||
|
6
extern/audaspace/include/IReader.h
vendored
6
extern/audaspace/include/IReader.h
vendored
@@ -70,12 +70,6 @@ public:
|
||||
*/
|
||||
virtual int getPosition() const=0;
|
||||
|
||||
/**
|
||||
* Returns the start offset the sound should have to line up with related sources.
|
||||
* \return The required start offset in seconds.
|
||||
*/
|
||||
virtual double getStartOffset() const { return 0.0;}
|
||||
|
||||
/**
|
||||
* Returns the specification of the reader.
|
||||
* \return The Specs structure.
|
||||
|
23
extern/audaspace/include/file/File.h
vendored
23
extern/audaspace/include/file/File.h
vendored
@@ -23,9 +23,11 @@
|
||||
*/
|
||||
|
||||
#include "ISound.h"
|
||||
#include "FileInfo.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
@@ -48,6 +50,14 @@ private:
|
||||
*/
|
||||
std::shared_ptr<Buffer> m_buffer;
|
||||
|
||||
/**
|
||||
* The index of the stream within the file if it contains multiple.
|
||||
* The first audio stream in the file has index 0 and the index increments by one
|
||||
* for every other audio stream in the file. Other types of streams in the file
|
||||
* do not count.
|
||||
*/
|
||||
int m_stream;
|
||||
|
||||
// delete copy constructor and operator=
|
||||
File(const File&) = delete;
|
||||
File& operator=(const File&) = delete;
|
||||
@@ -57,16 +67,25 @@ public:
|
||||
* Creates a new sound.
|
||||
* The file is read from the file system using the given path.
|
||||
* \param filename The sound file path.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
*/
|
||||
File(std::string filename);
|
||||
File(std::string filename, int stream = 0);
|
||||
|
||||
/**
|
||||
* Creates a new sound.
|
||||
* The file is read from memory using the supplied buffer.
|
||||
* \param buffer The buffer to read from.
|
||||
* \param size The size of the buffer.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
*/
|
||||
File(const data_t* buffer, int size);
|
||||
File(const data_t* buffer, int size, int stream = 0);
|
||||
|
||||
/**
|
||||
* Queries the streams of the file.
|
||||
* \return A vector with as many streams as there are in the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
std::vector<StreamInfo> queryStreams();
|
||||
|
||||
virtual std::shared_ptr<IReader> createReader();
|
||||
};
|
||||
|
42
extern/audaspace/include/file/FileInfo.h
vendored
Normal file
42
extern/audaspace/include/file/FileInfo.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*******************************************************************************
|
||||
* Copyright 2009-2016 Jörg Müller
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file FileInfo.h
|
||||
* @ingroup file
|
||||
* The FileInfo data structures.
|
||||
*/
|
||||
|
||||
#include "respec/Specification.h"
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
/// Specification of a sound source.
|
||||
struct StreamInfo
|
||||
{
|
||||
/// Start time in seconds.
|
||||
double start;
|
||||
|
||||
/// Duration in seconds. May be estimated or 0 if unknown.
|
||||
double duration;
|
||||
|
||||
/// Audio data parameters.
|
||||
DeviceSpecs specs;
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
24
extern/audaspace/include/file/FileManager.h
vendored
24
extern/audaspace/include/file/FileManager.h
vendored
@@ -22,12 +22,14 @@
|
||||
* The FileManager class.
|
||||
*/
|
||||
|
||||
#include "FileInfo.h"
|
||||
#include "respec/Specification.h"
|
||||
#include "IWriter.h"
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
@@ -66,18 +68,36 @@ public:
|
||||
/**
|
||||
* Creates a file reader for the given filename if a registed IFileInput is able to read it.
|
||||
* @param filename The path to the file.
|
||||
* @param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* @return The reader created.
|
||||
* @exception Exception If no file input can read the file an exception is thrown.
|
||||
*/
|
||||
static std::shared_ptr<IReader> createReader(std::string filename);
|
||||
static std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
|
||||
|
||||
/**
|
||||
* Creates a file reader for the given buffer if a registed IFileInput is able to read it.
|
||||
* @param buffer The buffer to read the file from.
|
||||
* @param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* @return The reader created.
|
||||
* @exception Exception If no file input can read the file an exception is thrown.
|
||||
*/
|
||||
static std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
|
||||
static std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
|
||||
|
||||
/**
|
||||
* Queries the streams of a sound file.
|
||||
* \param filename Path to the file to be read.
|
||||
* \return A vector with as many streams as there are in the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
static std::vector<StreamInfo> queryStreams(std::string filename);
|
||||
|
||||
/**
|
||||
* Queries the streams of a sound file.
|
||||
* \param buffer The in-memory file buffer.
|
||||
* \return A vector with as many streams as there are in the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
static std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
|
||||
|
||||
/**
|
||||
* Creates a file writer that writes a sound to the given file path.
|
||||
|
24
extern/audaspace/include/file/IFileInput.h
vendored
24
extern/audaspace/include/file/IFileInput.h
vendored
@@ -23,9 +23,11 @@
|
||||
*/
|
||||
|
||||
#include "Audaspace.h"
|
||||
#include "FileInfo.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
@@ -48,18 +50,36 @@ public:
|
||||
/**
|
||||
* Creates a reader for a file to be read.
|
||||
* \param filename Path to the file to be read.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \return The reader that reads the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
virtual std::shared_ptr<IReader> createReader(std::string filename)=0;
|
||||
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0)=0;
|
||||
|
||||
/**
|
||||
* Creates a reader for a file to be read from memory.
|
||||
* \param buffer The in-memory file buffer.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \return The reader that reads the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer)=0;
|
||||
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0)=0;
|
||||
|
||||
/**
|
||||
* Queries the streams of a sound file.
|
||||
* \param filename Path to the file to be read.
|
||||
* \return A vector with as many streams as there are in the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
virtual std::vector<StreamInfo> queryStreams(std::string filename)=0;
|
||||
|
||||
/**
|
||||
* Queries the streams of a sound file.
|
||||
* \param buffer The in-memory file buffer.
|
||||
* \return A vector with as many streams as there are in the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer)=0;
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
|
2
extern/audaspace/include/fx/VolumeReader.h
vendored
2
extern/audaspace/include/fx/VolumeReader.h
vendored
@@ -67,4 +67,4 @@ public:
|
||||
virtual void read(int& length, bool& eos, sample_t* buffer);
|
||||
};
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
AUD_NAMESPACE_END
|
18
extern/audaspace/plugins/ffmpeg/FFMPEG.cpp
vendored
18
extern/audaspace/plugins/ffmpeg/FFMPEG.cpp
vendored
@@ -35,14 +35,24 @@ void FFMPEG::registerPlugin()
|
||||
FileManager::registerOutput(plugin);
|
||||
}
|
||||
|
||||
std::shared_ptr<IReader> FFMPEG::createReader(std::string filename)
|
||||
std::shared_ptr<IReader> FFMPEG::createReader(std::string filename, int stream)
|
||||
{
|
||||
return std::shared_ptr<IReader>(new FFMPEGReader(filename));
|
||||
return std::shared_ptr<IReader>(new FFMPEGReader(filename, stream));
|
||||
}
|
||||
|
||||
std::shared_ptr<IReader> FFMPEG::createReader(std::shared_ptr<Buffer> buffer)
|
||||
std::shared_ptr<IReader> FFMPEG::createReader(std::shared_ptr<Buffer> buffer, int stream)
|
||||
{
|
||||
return std::shared_ptr<IReader>(new FFMPEGReader(buffer));
|
||||
return std::shared_ptr<IReader>(new FFMPEGReader(buffer, stream));
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> FFMPEG::queryStreams(std::string filename)
|
||||
{
|
||||
return FFMPEGReader(filename).queryStreams();
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> FFMPEG::queryStreams(std::shared_ptr<Buffer> buffer)
|
||||
{
|
||||
return FFMPEGReader(buffer).queryStreams();
|
||||
}
|
||||
|
||||
std::shared_ptr<IWriter> FFMPEG::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
|
||||
|
6
extern/audaspace/plugins/ffmpeg/FFMPEG.h
vendored
6
extern/audaspace/plugins/ffmpeg/FFMPEG.h
vendored
@@ -52,8 +52,10 @@ public:
|
||||
*/
|
||||
static void registerPlugin();
|
||||
|
||||
virtual std::shared_ptr<IReader> createReader(std::string filename);
|
||||
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
|
||||
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
|
||||
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
|
||||
virtual std::vector<StreamInfo> queryStreams(std::string filename);
|
||||
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
|
||||
virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
|
||||
};
|
||||
|
||||
|
169
extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp
vendored
169
extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp
vendored
@@ -31,6 +31,25 @@ AUD_NAMESPACE_BEGIN
|
||||
#define FFMPEG_OLD_CODE
|
||||
#endif
|
||||
|
||||
SampleFormat FFMPEGReader::convertSampleFormat(AVSampleFormat format)
|
||||
{
|
||||
switch(av_get_packed_sample_fmt(format))
|
||||
{
|
||||
case AV_SAMPLE_FMT_U8:
|
||||
return FORMAT_U8;
|
||||
case AV_SAMPLE_FMT_S16:
|
||||
return FORMAT_S16;
|
||||
case AV_SAMPLE_FMT_S32:
|
||||
return FORMAT_S32;
|
||||
case AV_SAMPLE_FMT_FLT:
|
||||
return FORMAT_FLOAT32;
|
||||
case AV_SAMPLE_FMT_DBL:
|
||||
return FORMAT_FLOAT64;
|
||||
default:
|
||||
AUD_THROW(FileException, "FFMPEG sample format unknown.");
|
||||
}
|
||||
}
|
||||
|
||||
int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
|
||||
{
|
||||
int buf_size = buffer.getSize();
|
||||
@@ -68,7 +87,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
|
||||
for(int i = 0; i < m_frame->nb_samples; i++)
|
||||
{
|
||||
std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size,
|
||||
m_frame->data[channel] + i * single_size, single_size);
|
||||
m_frame->data[channel] + i * single_size, single_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +128,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
|
||||
for(int i = 0; i < m_frame->nb_samples; i++)
|
||||
{
|
||||
std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size,
|
||||
m_frame->data[channel] + i * single_size, single_size);
|
||||
m_frame->data[channel] + i * single_size, single_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,13 +142,10 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer)
|
||||
return buf_pos;
|
||||
}
|
||||
|
||||
void FFMPEGReader::init()
|
||||
void FFMPEGReader::init(int stream)
|
||||
{
|
||||
m_position = 0;
|
||||
m_start_offset = 0.0f;
|
||||
m_pkgbuf_left = 0;
|
||||
m_st_time = 0;
|
||||
m_duration = 0;
|
||||
|
||||
if(avformat_find_stream_info(m_formatCtx, nullptr) < 0)
|
||||
AUD_THROW(FileException, "File couldn't be read, ffmpeg couldn't find the stream info.");
|
||||
@@ -137,43 +153,22 @@ void FFMPEGReader::init()
|
||||
// find audio stream and codec
|
||||
m_stream = -1;
|
||||
|
||||
double dur_sec = 0;
|
||||
|
||||
for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++)
|
||||
{
|
||||
#ifdef FFMPEG_OLD_CODE
|
||||
if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
if((m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
#else
|
||||
if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
if((m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
#endif
|
||||
&& (m_stream < 0))
|
||||
{
|
||||
AVStream *audio_stream = m_formatCtx->streams[i];
|
||||
double audio_timebase = av_q2d(audio_stream->time_base);
|
||||
|
||||
if (audio_stream->start_time != AV_NOPTS_VALUE)
|
||||
if(stream == 0)
|
||||
{
|
||||
m_st_time = audio_stream->start_time;
|
||||
}
|
||||
|
||||
int64_t ctx_start_time = 0;
|
||||
if (m_formatCtx->start_time != AV_NOPTS_VALUE) {
|
||||
ctx_start_time = m_formatCtx->start_time;
|
||||
}
|
||||
|
||||
m_start_offset = m_st_time * audio_timebase - (double)ctx_start_time / AV_TIME_BASE;
|
||||
|
||||
if(audio_stream->duration != AV_NOPTS_VALUE)
|
||||
{
|
||||
dur_sec = audio_stream->duration * audio_timebase;
|
||||
m_stream=i;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the audio starts after the stream start time, subract this from the total duration. */
|
||||
dur_sec = (double)m_formatCtx->duration / AV_TIME_BASE - m_start_offset;
|
||||
}
|
||||
|
||||
m_stream=i;
|
||||
break;
|
||||
stream--;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,10 +237,9 @@ void FFMPEGReader::init()
|
||||
}
|
||||
|
||||
m_specs.rate = (SampleRate) m_codecCtx->sample_rate;
|
||||
m_duration = lround(dur_sec * m_codecCtx->sample_rate);
|
||||
}
|
||||
|
||||
FFMPEGReader::FFMPEGReader(std::string filename) :
|
||||
FFMPEGReader::FFMPEGReader(std::string filename, int stream) :
|
||||
m_pkgbuf(),
|
||||
m_formatCtx(nullptr),
|
||||
m_codecCtx(nullptr),
|
||||
@@ -259,7 +253,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) :
|
||||
|
||||
try
|
||||
{
|
||||
init();
|
||||
init(stream);
|
||||
}
|
||||
catch(Exception&)
|
||||
{
|
||||
@@ -268,7 +262,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) :
|
||||
}
|
||||
}
|
||||
|
||||
FFMPEGReader::FFMPEGReader(std::shared_ptr<Buffer> buffer) :
|
||||
FFMPEGReader::FFMPEGReader(std::shared_ptr<Buffer> buffer, int stream) :
|
||||
m_pkgbuf(),
|
||||
m_codecCtx(nullptr),
|
||||
m_frame(nullptr),
|
||||
@@ -295,7 +289,7 @@ FFMPEGReader::FFMPEGReader(std::shared_ptr<Buffer> buffer) :
|
||||
|
||||
try
|
||||
{
|
||||
init();
|
||||
init(stream);
|
||||
}
|
||||
catch(Exception&)
|
||||
{
|
||||
@@ -318,6 +312,51 @@ FFMPEGReader::~FFMPEGReader()
|
||||
avformat_close_input(&m_formatCtx);
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> FFMPEGReader::queryStreams()
|
||||
{
|
||||
std::vector<StreamInfo> result;
|
||||
|
||||
for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++)
|
||||
{
|
||||
#ifdef FFMPEG_OLD_CODE
|
||||
if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
#else
|
||||
if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
|
||||
#endif
|
||||
{
|
||||
StreamInfo info;
|
||||
|
||||
double time_base = av_q2d(m_formatCtx->streams[i]->time_base);
|
||||
|
||||
if(m_formatCtx->streams[i]->start_time != AV_NOPTS_VALUE)
|
||||
info.start = m_formatCtx->streams[i]->start_time * time_base;
|
||||
else
|
||||
info.start = 0;
|
||||
|
||||
if(m_formatCtx->streams[i]->duration != AV_NOPTS_VALUE)
|
||||
info.duration = m_formatCtx->streams[i]->duration * time_base;
|
||||
else if(m_formatCtx->duration != AV_NOPTS_VALUE)
|
||||
info.duration = double(m_formatCtx->duration) / AV_TIME_BASE - info.start;
|
||||
else
|
||||
info.duration = 0;
|
||||
|
||||
#ifdef FFMPEG_OLD_CODE
|
||||
info.specs.channels = Channels(m_formatCtx->streams[i]->codec->channels);
|
||||
info.specs.rate = m_formatCtx->streams[i]->codec->sample_rate;
|
||||
info.specs.format = convertSampleFormat(m_formatCtx->streams[i]->codec->sample_fmt);
|
||||
#else
|
||||
info.specs.channels = Channels(m_formatCtx->streams[i]->codecpar->channels);
|
||||
info.specs.rate = m_formatCtx->streams[i]->codecpar->sample_rate;
|
||||
info.specs.format = convertSampleFormat(AVSampleFormat(m_formatCtx->streams[i]->codecpar->format));
|
||||
#endif
|
||||
|
||||
result.emplace_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int FFMPEGReader::read_packet(void* opaque, uint8_t* buf, int buf_size)
|
||||
{
|
||||
FFMPEGReader* reader = reinterpret_cast<FFMPEGReader*>(opaque);
|
||||
@@ -368,18 +407,16 @@ void FFMPEGReader::seek(int position)
|
||||
{
|
||||
if(position >= 0)
|
||||
{
|
||||
double pts_time_base =
|
||||
av_q2d(m_formatCtx->streams[m_stream]->time_base);
|
||||
double pts_time_base = av_q2d(m_formatCtx->streams[m_stream]->time_base);
|
||||
|
||||
uint64_t seek_pts = (((uint64_t)position) / ((uint64_t)m_specs.rate)) / pts_time_base;
|
||||
uint64_t st_time = m_formatCtx->streams[m_stream]->start_time;
|
||||
uint64_t seek_pos = (uint64_t)(position / (pts_time_base * m_specs.rate));
|
||||
|
||||
if(m_st_time != AV_NOPTS_VALUE) {
|
||||
seek_pts += m_st_time;
|
||||
}
|
||||
if(st_time != AV_NOPTS_VALUE)
|
||||
seek_pos += st_time;
|
||||
|
||||
// a value < 0 tells us that seeking failed
|
||||
if(av_seek_frame(m_formatCtx, m_stream, seek_pts,
|
||||
AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0)
|
||||
if(av_seek_frame(m_formatCtx, m_stream, seek_pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0)
|
||||
{
|
||||
avcodec_flush_buffers(m_codecCtx);
|
||||
m_position = position;
|
||||
@@ -400,7 +437,7 @@ void FFMPEGReader::seek(int position)
|
||||
if(packet.pts != AV_NOPTS_VALUE)
|
||||
{
|
||||
// calculate real position, and read to frame!
|
||||
m_position = (packet.pts - m_st_time) * pts_time_base * m_specs.rate;
|
||||
m_position = (packet.pts - (st_time != AV_NOPTS_VALUE ? st_time : 0)) * pts_time_base * m_specs.rate;
|
||||
|
||||
if(m_position < position)
|
||||
{
|
||||
@@ -430,8 +467,25 @@ void FFMPEGReader::seek(int position)
|
||||
|
||||
int FFMPEGReader::getLength() const
|
||||
{
|
||||
auto stream = m_formatCtx->streams[m_stream];
|
||||
|
||||
double time_base = av_q2d(stream->time_base);
|
||||
double duration;
|
||||
|
||||
if(stream->duration != AV_NOPTS_VALUE)
|
||||
duration = stream->duration * time_base;
|
||||
else if(m_formatCtx->duration != AV_NOPTS_VALUE)
|
||||
{
|
||||
duration = float(m_formatCtx->duration) / AV_TIME_BASE;
|
||||
|
||||
if(stream->start_time != AV_NOPTS_VALUE)
|
||||
duration -= stream->start_time * time_base;
|
||||
}
|
||||
else
|
||||
duration = -1;
|
||||
|
||||
// return approximated remaning size
|
||||
return m_duration - m_position;
|
||||
return (int)(duration * m_codecCtx->sample_rate) - m_position;
|
||||
}
|
||||
|
||||
int FFMPEGReader::getPosition() const
|
||||
@@ -439,11 +493,6 @@ int FFMPEGReader::getPosition() const
|
||||
return m_position;
|
||||
}
|
||||
|
||||
double FFMPEGReader::getStartOffset() const
|
||||
{
|
||||
return m_start_offset;
|
||||
}
|
||||
|
||||
Specs FFMPEGReader::getSpecs() const
|
||||
{
|
||||
return m_specs.specs;
|
||||
@@ -480,13 +529,11 @@ void FFMPEGReader::read(int& length, bool& eos, sample_t* buffer)
|
||||
// decode the package
|
||||
pkgbuf_pos = decode(packet, m_pkgbuf);
|
||||
|
||||
if (packet.pts >= m_st_time) {
|
||||
// copy to output buffer
|
||||
data_size = std::min(pkgbuf_pos, left * sample_size);
|
||||
m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format));
|
||||
buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
|
||||
left -= data_size / sample_size;
|
||||
}
|
||||
// copy to output buffer
|
||||
data_size = std::min(pkgbuf_pos, left * sample_size);
|
||||
m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format));
|
||||
buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
|
||||
left -= data_size / sample_size;
|
||||
}
|
||||
av_packet_unref(&packet);
|
||||
}
|
||||
|
42
extern/audaspace/plugins/ffmpeg/FFMPEGReader.h
vendored
42
extern/audaspace/plugins/ffmpeg/FFMPEGReader.h
vendored
@@ -29,9 +29,11 @@
|
||||
#include "respec/ConverterFunctions.h"
|
||||
#include "IReader.h"
|
||||
#include "util/Buffer.h"
|
||||
#include "file/FileInfo.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
struct AVCodecContext;
|
||||
extern "C" {
|
||||
@@ -54,22 +56,6 @@ private:
|
||||
*/
|
||||
int m_position;
|
||||
|
||||
/**
|
||||
* The start offset in seconds relative to the media container start time.
|
||||
* IE how much the sound should be delayed to be kept in sync with the rest of the containter streams.
|
||||
*/
|
||||
double m_start_offset;
|
||||
|
||||
/**
|
||||
* The start time pts of the stream. All packets before this timestamp shouldn't be played back (only decoded).
|
||||
*/
|
||||
int64_t m_st_time;
|
||||
|
||||
/**
|
||||
* The duration of the audio stream in samples.
|
||||
*/
|
||||
int64_t m_duration;
|
||||
|
||||
/**
|
||||
* The specification of the audio data.
|
||||
*/
|
||||
@@ -135,6 +121,13 @@ private:
|
||||
*/
|
||||
bool m_tointerleave;
|
||||
|
||||
/**
|
||||
* Converts an ffmpeg sample format to an audaspace one.
|
||||
* \param format The AVSampleFormat sample format.
|
||||
* \return The sample format as SampleFormat.
|
||||
*/
|
||||
AUD_LOCAL static SampleFormat convertSampleFormat(AVSampleFormat format);
|
||||
|
||||
/**
|
||||
* Decodes a packet into the given buffer.
|
||||
* \param packet The AVPacket to decode.
|
||||
@@ -145,8 +138,9 @@ private:
|
||||
|
||||
/**
|
||||
* Initializes the object.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
*/
|
||||
AUD_LOCAL void init();
|
||||
AUD_LOCAL void init(int stream);
|
||||
|
||||
// delete copy constructor and operator=
|
||||
FFMPEGReader(const FFMPEGReader&) = delete;
|
||||
@@ -156,24 +150,33 @@ public:
|
||||
/**
|
||||
* Creates a new reader.
|
||||
* \param filename The path to the file to be read.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \exception Exception Thrown if the file specified does not exist or
|
||||
* cannot be read with ffmpeg.
|
||||
*/
|
||||
FFMPEGReader(std::string filename);
|
||||
FFMPEGReader(std::string filename, int stream = 0);
|
||||
|
||||
/**
|
||||
* Creates a new reader.
|
||||
* \param buffer The buffer to read from.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \exception Exception Thrown if the buffer specified cannot be read
|
||||
* with ffmpeg.
|
||||
*/
|
||||
FFMPEGReader(std::shared_ptr<Buffer> buffer);
|
||||
FFMPEGReader(std::shared_ptr<Buffer> buffer, int stream = 0);
|
||||
|
||||
/**
|
||||
* Destroys the reader and closes the file.
|
||||
*/
|
||||
virtual ~FFMPEGReader();
|
||||
|
||||
/**
|
||||
* Queries the streams of a sound file.
|
||||
* \return A vector with as many streams as there are in the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
virtual std::vector<StreamInfo> queryStreams();
|
||||
|
||||
/**
|
||||
* Reads data to a memory buffer.
|
||||
* This function is used for avio only.
|
||||
@@ -198,7 +201,6 @@ public:
|
||||
virtual void seek(int position);
|
||||
virtual int getLength() const;
|
||||
virtual int getPosition() const;
|
||||
virtual double getStartOffset() const;
|
||||
virtual Specs getSpecs() const;
|
||||
virtual void read(int& length, bool& eos, sample_t* buffer);
|
||||
};
|
||||
|
14
extern/audaspace/plugins/libsndfile/SndFile.cpp
vendored
14
extern/audaspace/plugins/libsndfile/SndFile.cpp
vendored
@@ -32,16 +32,26 @@ void SndFile::registerPlugin()
|
||||
FileManager::registerOutput(plugin);
|
||||
}
|
||||
|
||||
std::shared_ptr<IReader> SndFile::createReader(std::string filename)
|
||||
std::shared_ptr<IReader> SndFile::createReader(std::string filename, int stream)
|
||||
{
|
||||
return std::shared_ptr<IReader>(new SndFileReader(filename));
|
||||
}
|
||||
|
||||
std::shared_ptr<IReader> SndFile::createReader(std::shared_ptr<Buffer> buffer)
|
||||
std::shared_ptr<IReader> SndFile::createReader(std::shared_ptr<Buffer> buffer, int stream)
|
||||
{
|
||||
return std::shared_ptr<IReader>(new SndFileReader(buffer));
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> SndFile::queryStreams(std::string filename)
|
||||
{
|
||||
return SndFileReader(filename).queryStreams();
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> SndFile::queryStreams(std::shared_ptr<Buffer> buffer)
|
||||
{
|
||||
return SndFileReader(buffer).queryStreams();
|
||||
}
|
||||
|
||||
std::shared_ptr<IWriter> SndFile::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate)
|
||||
{
|
||||
return std::shared_ptr<IWriter>(new SndFileWriter(filename, specs, format, codec, bitrate));
|
||||
|
@@ -52,8 +52,10 @@ public:
|
||||
*/
|
||||
static void registerPlugin();
|
||||
|
||||
virtual std::shared_ptr<IReader> createReader(std::string filename);
|
||||
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer);
|
||||
virtual std::shared_ptr<IReader> createReader(std::string filename, int stream = 0);
|
||||
virtual std::shared_ptr<IReader> createReader(std::shared_ptr<Buffer> buffer, int stream = 0);
|
||||
virtual std::vector<StreamInfo> queryStreams(std::string filename);
|
||||
virtual std::vector<StreamInfo> queryStreams(std::shared_ptr<Buffer> buffer);
|
||||
virtual std::shared_ptr<IWriter> createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate);
|
||||
};
|
||||
|
||||
|
@@ -118,6 +118,21 @@ SndFileReader::~SndFileReader()
|
||||
sf_close(m_sndfile);
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> SndFileReader::queryStreams()
|
||||
{
|
||||
std::vector<StreamInfo> result;
|
||||
|
||||
StreamInfo info;
|
||||
info.start = 0;
|
||||
info.duration = double(getLength()) / m_specs.rate;
|
||||
info.specs.specs = m_specs;
|
||||
info.specs.format = FORMAT_FLOAT32;
|
||||
|
||||
result.emplace_back(info);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool SndFileReader::isSeekable() const
|
||||
{
|
||||
return m_seekable;
|
||||
|
@@ -28,9 +28,12 @@
|
||||
* The SndFileReader class.
|
||||
*/
|
||||
|
||||
#include "file/FileInfo.h"
|
||||
|
||||
#include <string>
|
||||
#include <sndfile.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
@@ -96,6 +99,7 @@ public:
|
||||
/**
|
||||
* Creates a new reader.
|
||||
* \param filename The path to the file to be read.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \exception Exception Thrown if the file specified does not exist or
|
||||
* cannot be read with libsndfile.
|
||||
*/
|
||||
@@ -104,6 +108,7 @@ public:
|
||||
/**
|
||||
* Creates a new reader.
|
||||
* \param buffer The buffer to read from.
|
||||
* \param stream The index of the audio stream within the file if it contains multiple audio streams.
|
||||
* \exception Exception Thrown if the buffer specified cannot be read
|
||||
* with libsndfile.
|
||||
*/
|
||||
@@ -114,6 +119,13 @@ public:
|
||||
*/
|
||||
virtual ~SndFileReader();
|
||||
|
||||
/**
|
||||
* Queries the streams of a sound file.
|
||||
* \return A vector with as many streams as there are in the file.
|
||||
* \exception Exception Thrown if the file specified cannot be read.
|
||||
*/
|
||||
virtual std::vector<StreamInfo> queryStreams();
|
||||
|
||||
virtual bool isSeekable() const;
|
||||
virtual void seek(int position);
|
||||
virtual int getLength() const;
|
||||
|
20
extern/audaspace/src/file/File.cpp
vendored
20
extern/audaspace/src/file/File.cpp
vendored
@@ -23,23 +23,31 @@
|
||||
|
||||
AUD_NAMESPACE_BEGIN
|
||||
|
||||
File::File(std::string filename) :
|
||||
m_filename(filename)
|
||||
File::File(std::string filename, int stream) :
|
||||
m_filename(filename), m_stream(stream)
|
||||
{
|
||||
}
|
||||
|
||||
File::File(const data_t* buffer, int size) :
|
||||
m_buffer(new Buffer(size))
|
||||
File::File(const data_t* buffer, int size, int stream) :
|
||||
m_buffer(new Buffer(size)), m_stream(stream)
|
||||
{
|
||||
std::memcpy(m_buffer->getBuffer(), buffer, size);
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> File::queryStreams()
|
||||
{
|
||||
if(m_buffer.get())
|
||||
return FileManager::queryStreams(m_buffer);
|
||||
else
|
||||
return FileManager::queryStreams(m_filename);
|
||||
}
|
||||
|
||||
std::shared_ptr<IReader> File::createReader()
|
||||
{
|
||||
if(m_buffer.get())
|
||||
return FileManager::createReader(m_buffer);
|
||||
return FileManager::createReader(m_buffer, m_stream);
|
||||
else
|
||||
return FileManager::createReader(m_filename);
|
||||
return FileManager::createReader(m_filename, m_stream);
|
||||
}
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
|
36
extern/audaspace/src/file/FileManager.cpp
vendored
36
extern/audaspace/src/file/FileManager.cpp
vendored
@@ -43,13 +43,13 @@ void FileManager::registerOutput(std::shared_ptr<aud::IFileOutput> output)
|
||||
outputs().push_back(output);
|
||||
}
|
||||
|
||||
std::shared_ptr<IReader> FileManager::createReader(std::string filename)
|
||||
std::shared_ptr<IReader> FileManager::createReader(std::string filename, int stream)
|
||||
{
|
||||
for(std::shared_ptr<IFileInput> input : inputs())
|
||||
{
|
||||
try
|
||||
{
|
||||
return input->createReader(filename);
|
||||
return input->createReader(filename, stream);
|
||||
}
|
||||
catch(Exception&) {}
|
||||
}
|
||||
@@ -57,13 +57,41 @@ std::shared_ptr<IReader> FileManager::createReader(std::string filename)
|
||||
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
|
||||
}
|
||||
|
||||
std::shared_ptr<IReader> FileManager::createReader(std::shared_ptr<Buffer> buffer)
|
||||
std::shared_ptr<IReader> FileManager::createReader(std::shared_ptr<Buffer> buffer, int stream)
|
||||
{
|
||||
for(std::shared_ptr<IFileInput> input : inputs())
|
||||
{
|
||||
try
|
||||
{
|
||||
return input->createReader(buffer);
|
||||
return input->createReader(buffer, stream);
|
||||
}
|
||||
catch(Exception&) {}
|
||||
}
|
||||
|
||||
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> FileManager::queryStreams(std::string filename)
|
||||
{
|
||||
for(std::shared_ptr<IFileInput> input : inputs())
|
||||
{
|
||||
try
|
||||
{
|
||||
return input->queryStreams(filename);
|
||||
}
|
||||
catch(Exception&) {}
|
||||
}
|
||||
|
||||
AUD_THROW(FileException, "The file couldn't be read with any installed file reader.");
|
||||
}
|
||||
|
||||
std::vector<StreamInfo> FileManager::queryStreams(std::shared_ptr<Buffer> buffer)
|
||||
{
|
||||
for(std::shared_ptr<IFileInput> input : inputs())
|
||||
{
|
||||
try
|
||||
{
|
||||
return input->queryStreams(buffer);
|
||||
}
|
||||
catch(Exception&) {}
|
||||
}
|
||||
|
2
extern/audaspace/src/fx/VolumeReader.cpp
vendored
2
extern/audaspace/src/fx/VolumeReader.cpp
vendored
@@ -57,4 +57,4 @@ void VolumeReader::read(int& length, bool& eos, sample_t* buffer)
|
||||
buffer[i] = buffer[i] * m_volumeStorage->getVolume();
|
||||
}
|
||||
|
||||
AUD_NAMESPACE_END
|
||||
AUD_NAMESPACE_END
|
Submodule release/datafiles/locale updated: 8a05b618f0...94c39b5832
@@ -231,7 +231,7 @@ const UserDef U_default = {
|
||||
.collection_instance_empty_size = 1.0f,
|
||||
|
||||
.statusbar_flag = STATUSBAR_SHOW_VERSION,
|
||||
.file_preview_type = USER_FILE_PREVIEW_CAMERA,
|
||||
.file_preview_type = USER_FILE_PREVIEW_AUTO,
|
||||
|
||||
.runtime =
|
||||
{
|
||||
|
Submodule release/scripts/addons updated: 67f1fbca14...ecf30de46c
Submodule release/scripts/addons_contrib updated: ef6ef414d2...42da56aa73
@@ -63,16 +63,11 @@ def kmi_args_as_data(kmi):
|
||||
if kmi.any:
|
||||
s.append("\"any\": True")
|
||||
else:
|
||||
if kmi.shift:
|
||||
s.append("\"shift\": True")
|
||||
if kmi.ctrl:
|
||||
s.append("\"ctrl\": True")
|
||||
if kmi.alt:
|
||||
s.append("\"alt\": True")
|
||||
if kmi.oskey:
|
||||
s.append("\"oskey\": True")
|
||||
if kmi.key_modifier and kmi.key_modifier != 'NONE':
|
||||
s.append(f"\"key_modifier\": '{kmi.key_modifier}'")
|
||||
for attr in ("shift", "ctrl", "alt", "oskey"):
|
||||
if mod := getattr(kmi, attr):
|
||||
s.append(f"\"{attr:s}\": " + ("-1" if mod == -1 else "True"))
|
||||
if (mod := kmi.key_modifier) and (mod != 'NONE'):
|
||||
s.append(f"\"key_modifier\": '{mod:s}'")
|
||||
|
||||
if kmi.repeat:
|
||||
if (
|
||||
|
@@ -199,10 +199,12 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level):
|
||||
subrow = sub.row()
|
||||
subrow.scale_x = 0.75
|
||||
subrow.prop(kmi, "any", toggle=True)
|
||||
subrow.prop(kmi, "shift", toggle=True)
|
||||
subrow.prop(kmi, "ctrl", toggle=True)
|
||||
subrow.prop(kmi, "alt", toggle=True)
|
||||
subrow.prop(kmi, "oskey", text="Cmd", toggle=True)
|
||||
# Use `*_ui` properties as integers aren't practical.
|
||||
subrow.prop(kmi, "shift_ui", toggle=True)
|
||||
subrow.prop(kmi, "ctrl_ui", toggle=True)
|
||||
subrow.prop(kmi, "alt_ui", toggle=True)
|
||||
subrow.prop(kmi, "oskey_ui", text="Cmd", toggle=True)
|
||||
|
||||
subrow.prop(kmi, "key_modifier", text="", event=True)
|
||||
|
||||
# Operator properties
|
||||
|
@@ -85,9 +85,9 @@ class ASSET_OT_open_containing_blend_file(Operator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
asset_file_handle = getattr(context, 'asset_file_handle', None)
|
||||
asset_library = getattr(context, 'asset_library', None)
|
||||
asset_library_ref = getattr(context, 'asset_library_ref', None)
|
||||
|
||||
if not asset_library:
|
||||
if not asset_library_ref:
|
||||
cls.poll_message_set("No asset library selected")
|
||||
return False
|
||||
if not asset_file_handle:
|
||||
@@ -100,13 +100,13 @@ class ASSET_OT_open_containing_blend_file(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
asset_file_handle = context.asset_file_handle
|
||||
asset_library = context.asset_library
|
||||
asset_library_ref = context.asset_library_ref
|
||||
|
||||
if asset_file_handle.local_id:
|
||||
self.report({'WARNING'}, "This asset is stored in the current blend file")
|
||||
return {'CANCELLED'}
|
||||
|
||||
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library)
|
||||
asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref)
|
||||
self.open_in_new_blender(asset_lib_path)
|
||||
|
||||
wm = context.window_manager
|
||||
|
@@ -168,8 +168,8 @@ class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel):
|
||||
col.prop(freestyle, "as_render_pass", text="As Render Pass")
|
||||
|
||||
|
||||
class VIEWLAYER_PT_freestyle_edge_detection_options(ViewLayerFreestyleButtonsPanel, Panel):
|
||||
bl_label = "Edge Detection Options"
|
||||
class VIEWLAYER_PT_freestyle_edge_detection(ViewLayerFreestyleButtonsPanel, Panel):
|
||||
bl_label = "Edge Detection"
|
||||
bl_parent_id = "VIEWLAYER_PT_freestyle"
|
||||
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
||||
|
||||
@@ -435,7 +435,7 @@ class VIEWLAYER_PT_freestyle_lineset_collection(ViewLayerFreestyleLineStyle, Pan
|
||||
lineset = freestyle.linesets.active
|
||||
|
||||
layout.active = lineset.select_by_collection
|
||||
layout.row().prop(lineset, "collection", text="Freestyle Lineset Collection")
|
||||
layout.row().prop(lineset, "collection", text="Line Set Collection")
|
||||
layout.row().prop(lineset, "collection_negation", expand=True, text="Negation")
|
||||
|
||||
|
||||
@@ -1258,12 +1258,14 @@ class MATERIAL_PT_freestyle_line(MaterialFreestyleButtonsPanel, Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
mat = context.material
|
||||
|
||||
row = layout.row()
|
||||
row.prop(mat, "line_color", text="")
|
||||
row.prop(mat, "line_priority", text="Priority")
|
||||
col = layout.column()
|
||||
col.prop(mat, "line_color")
|
||||
col.prop(mat, "line_priority", text="Priority")
|
||||
|
||||
|
||||
classes = (
|
||||
@@ -1271,7 +1273,7 @@ classes = (
|
||||
VIEWLAYER_UL_linesets,
|
||||
RENDER_MT_lineset_context_menu,
|
||||
VIEWLAYER_PT_freestyle,
|
||||
VIEWLAYER_PT_freestyle_edge_detection_options,
|
||||
VIEWLAYER_PT_freestyle_edge_detection,
|
||||
VIEWLAYER_PT_freestyle_style_modules,
|
||||
VIEWLAYER_PT_freestyle_lineset,
|
||||
VIEWLAYER_PT_freestyle_lineset_visibilty,
|
||||
|
@@ -417,6 +417,7 @@ class MASK_MT_select(Menu):
|
||||
|
||||
layout.operator("mask.select_box")
|
||||
layout.operator("mask.select_circle")
|
||||
layout.operator_menu_enum("mask.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@@ -1578,6 +1578,7 @@ class CLIP_MT_select(Menu):
|
||||
|
||||
layout.operator("clip.select_box")
|
||||
layout.operator("clip.select_circle")
|
||||
layout.operator_menu_enum("clip.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
@@ -394,6 +394,7 @@ class DOPESHEET_MT_select(Menu):
|
||||
layout.operator("action.select_box", text="Box Select (Axis Range)").axis_range = True
|
||||
|
||||
layout.operator("action.select_circle")
|
||||
layout.operator_menu_enum("action.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("action.select_column", text="Columns on Selected Keys").mode = 'KEYS'
|
||||
|
@@ -179,6 +179,7 @@ class GRAPH_MT_select(Menu):
|
||||
props.include_handles = True
|
||||
|
||||
layout.operator("graph.select_circle")
|
||||
layout.operator_menu_enum("graph.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("graph.select_column", text="Columns on Selected Keys").mode = 'KEYS'
|
||||
|
@@ -289,6 +289,7 @@ class NODE_MT_select(Menu):
|
||||
|
||||
layout.operator("node.select_box").tweak = False
|
||||
layout.operator("node.select_circle")
|
||||
layout.operator_menu_enum("node.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
layout.operator("node.select_all").action = 'TOGGLE'
|
||||
|
@@ -209,9 +209,9 @@ class TOPBAR_MT_editor_menus(Menu):
|
||||
|
||||
# Allow calling this menu directly (this might not be a header area).
|
||||
if getattr(context.area, "show_menus", False):
|
||||
layout.menu("TOPBAR_MT_app", text="", icon='BLENDER')
|
||||
layout.menu("TOPBAR_MT_blender", text="", icon='BLENDER')
|
||||
else:
|
||||
layout.menu("TOPBAR_MT_app", text="Blender")
|
||||
layout.menu("TOPBAR_MT_blender", text="Blender")
|
||||
|
||||
layout.menu("TOPBAR_MT_file")
|
||||
layout.menu("TOPBAR_MT_edit")
|
||||
@@ -222,7 +222,7 @@ class TOPBAR_MT_editor_menus(Menu):
|
||||
layout.menu("TOPBAR_MT_help")
|
||||
|
||||
|
||||
class TOPBAR_MT_app(Menu):
|
||||
class TOPBAR_MT_blender(Menu):
|
||||
bl_label = "Blender"
|
||||
|
||||
def draw(self, _context):
|
||||
@@ -238,7 +238,7 @@ class TOPBAR_MT_app(Menu):
|
||||
|
||||
layout.separator()
|
||||
|
||||
layout.menu("TOPBAR_MT_app_system")
|
||||
layout.menu("TOPBAR_MT_blender_system")
|
||||
|
||||
|
||||
class TOPBAR_MT_file_cleanup(Menu):
|
||||
@@ -430,7 +430,7 @@ class TOPBAR_MT_file_defaults(Menu):
|
||||
|
||||
|
||||
# Include technical operators here which would otherwise have no way for users to access.
|
||||
class TOPBAR_MT_app_system(Menu):
|
||||
class TOPBAR_MT_blender_system(Menu):
|
||||
bl_label = "System"
|
||||
|
||||
def draw(self, _context):
|
||||
@@ -655,6 +655,7 @@ class TOPBAR_MT_window(Menu):
|
||||
layout.separator()
|
||||
|
||||
layout.operator("screen.screenshot")
|
||||
layout.operator("screen.screenshot_area")
|
||||
|
||||
if sys.platform[:3] == "win":
|
||||
layout.separator()
|
||||
@@ -854,8 +855,8 @@ classes = (
|
||||
TOPBAR_MT_file_context_menu,
|
||||
TOPBAR_MT_workspace_menu,
|
||||
TOPBAR_MT_editor_menus,
|
||||
TOPBAR_MT_app,
|
||||
TOPBAR_MT_app_system,
|
||||
TOPBAR_MT_blender,
|
||||
TOPBAR_MT_blender_system,
|
||||
TOPBAR_MT_file,
|
||||
TOPBAR_MT_file_new,
|
||||
TOPBAR_MT_file_recover,
|
||||
|
@@ -267,7 +267,6 @@ class USERPREF_PT_interface_editors(InterfacePanel, CenterAlignMixIn, Panel):
|
||||
|
||||
col = layout.column()
|
||||
col.prop(system, "use_region_overlap")
|
||||
col.prop(view, "show_layout_ui", text="Corner Splitting")
|
||||
col.prop(view, "show_navigate_ui")
|
||||
col.prop(view, "color_picker_type")
|
||||
col.row().prop(view, "header_align")
|
||||
|
@@ -1362,6 +1362,7 @@ class VIEW3D_MT_select_object(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1422,6 +1423,7 @@ class VIEW3D_MT_select_pose(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1456,6 +1458,7 @@ class VIEW3D_MT_select_particle(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1562,6 +1565,7 @@ class VIEW3D_MT_select_edit_mesh(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1615,6 +1619,7 @@ class VIEW3D_MT_select_edit_curve(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1650,6 +1655,7 @@ class VIEW3D_MT_select_edit_surface(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1711,6 +1717,7 @@ class VIEW3D_MT_select_edit_metaball(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1752,6 +1759,7 @@ class VIEW3D_MT_select_edit_lattice(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1782,6 +1790,7 @@ class VIEW3D_MT_select_edit_armature(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1849,6 +1858,7 @@ class VIEW3D_MT_select_gpencil(Menu):
|
||||
|
||||
layout.operator("gpencil.select_box")
|
||||
layout.operator("gpencil.select_circle")
|
||||
layout.operator_menu_enum("gpencil.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1885,6 +1895,7 @@ class VIEW3D_MT_select_paint_mask(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -1905,6 +1916,7 @@ class VIEW3D_MT_select_paint_mask_vertex(Menu):
|
||||
|
||||
layout.operator("view3d.select_box")
|
||||
layout.operator("view3d.select_circle")
|
||||
layout.operator_menu_enum("view3d.select_lasso", "mode")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -3983,6 +3995,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
|
||||
layout.operator("mesh.extrude_vertices_move", text="Extrude Vertices")
|
||||
layout.operator("mesh.dupli_extrude_cursor").rotate_source = True
|
||||
layout.operator("mesh.bevel", text="Bevel Vertices").affect = 'VERTICES'
|
||||
|
||||
layout.separator()
|
||||
@@ -4414,6 +4427,7 @@ class VIEW3D_MT_edit_curve_ctrlpoints(Menu):
|
||||
|
||||
if edit_object.type in {'CURVE', 'SURFACE'}:
|
||||
layout.operator("curve.extrude_move")
|
||||
layout.operator("curve.vertex_add")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -4742,6 +4756,7 @@ class VIEW3D_MT_edit_armature(Menu):
|
||||
layout.separator()
|
||||
|
||||
layout.operator("armature.extrude_move")
|
||||
layout.operator("armature.click_extrude")
|
||||
|
||||
if arm.use_mirror_x:
|
||||
layout.operator("armature.extrude_forked")
|
||||
|
@@ -500,8 +500,8 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_fields_legacy_poll),
|
||||
NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_fields_legacy_poll),
|
||||
NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_fields_legacy_poll),
|
||||
NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_fields_legacy_poll),
|
||||
|
||||
NodeItem("GeometryNodeAttributeRemove"),
|
||||
NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_COLOR", "Color", items=[
|
||||
@@ -526,6 +526,7 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeCurveFill"),
|
||||
NodeItem("GeometryNodeCurveTrim"),
|
||||
NodeItem("GeometryNodeCurveLength"),
|
||||
NodeItem("GeometryNodeCurveFillet"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[
|
||||
NodeItem("GeometryNodeCurvePrimitiveLine"),
|
||||
@@ -564,6 +565,8 @@ geometry_node_categories = [
|
||||
NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_fields_legacy_poll),
|
||||
NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_fields_legacy_poll),
|
||||
|
||||
NodeItem("GeometryNodeMaterialAssign", poll=geometry_nodes_fields_poll),
|
||||
NodeItem("GeometryNodeMaterialSelection", poll=geometry_nodes_fields_poll),
|
||||
NodeItem("GeometryNodeMaterialReplace"),
|
||||
]),
|
||||
GeometryNodeCategory("GEO_MESH", "Mesh", items=[
|
||||
|
@@ -92,6 +92,7 @@ set(SRC_DNA_INC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_texture_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_tracking_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_userdef_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_uuid_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vec_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_vfont_types.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/makesdna/DNA_view2d_types.h
|
||||
|
@@ -247,7 +247,7 @@ class OutputAttribute {
|
||||
GVMutableArrayPtr varray_;
|
||||
AttributeDomain domain_;
|
||||
SaveFn save_;
|
||||
std::optional<fn::GVMutableArray_GSpan> optional_span_varray_;
|
||||
std::unique_ptr<fn::GVMutableArray_GSpan> optional_span_varray_;
|
||||
bool ignore_old_values_ = false;
|
||||
bool save_has_been_called_ = false;
|
||||
|
||||
@@ -306,9 +306,10 @@ class OutputAttribute {
|
||||
|
||||
fn::GMutableSpan as_span()
|
||||
{
|
||||
if (!optional_span_varray_.has_value()) {
|
||||
if (!optional_span_varray_) {
|
||||
const bool materialize_old_values = !ignore_old_values_;
|
||||
optional_span_varray_.emplace(*varray_, materialize_old_values);
|
||||
optional_span_varray_ = std::make_unique<fn::GVMutableArray_GSpan>(*varray_,
|
||||
materialize_old_values);
|
||||
}
|
||||
fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_;
|
||||
return span_varray;
|
||||
|
@@ -103,6 +103,7 @@ class GeometryComponent {
|
||||
virtual int attribute_domain_size(const AttributeDomain domain) const;
|
||||
|
||||
bool attribute_is_builtin(const blender::StringRef attribute_name) const;
|
||||
bool attribute_is_builtin(const blender::bke::AttributeIDRef &attribute_id) const;
|
||||
|
||||
/* Get read-only access to the highest priority attribute with the given name.
|
||||
* Returns null if the attribute does not exist. */
|
||||
|
@@ -124,7 +124,10 @@ enum {
|
||||
/** Don't overwrite these flags when reading a file. */
|
||||
#define G_FLAG_ALL_RUNTIME \
|
||||
(G_FLAG_SCRIPT_AUTOEXEC | G_FLAG_SCRIPT_OVERRIDE_PREF | G_FLAG_EVENT_SIMULATE | \
|
||||
G_FLAG_USERPREF_NO_SAVE_ON_EXIT)
|
||||
G_FLAG_USERPREF_NO_SAVE_ON_EXIT | \
|
||||
\
|
||||
/* #BPY_python_reset is responsible for resetting these flags on file load. */ \
|
||||
G_FLAG_SCRIPT_AUTOEXEC_FAIL | G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)
|
||||
|
||||
/** Flags to read from blend file. */
|
||||
#define G_FLAG_ALL_READFILE 0
|
||||
|
@@ -45,8 +45,10 @@ enum {
|
||||
IDTYPE_FLAGS_NO_COPY = 1 << 0,
|
||||
/** Indicates that the given IDType does not support linking/appending from a library file. */
|
||||
IDTYPE_FLAGS_NO_LIBLINKING = 1 << 1,
|
||||
/** Indicates that the given IDType does not support making a library-linked ID local. */
|
||||
IDTYPE_FLAGS_NO_MAKELOCAL = 1 << 2,
|
||||
/** Indicates that the given IDType should not be directly linked from a library file, but may be
|
||||
* appended.
|
||||
* NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */
|
||||
IDTYPE_FLAGS_ONLY_APPEND = 1 << 2,
|
||||
/** Indicates that the given IDType does not have animation data. */
|
||||
IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3,
|
||||
};
|
||||
@@ -283,9 +285,14 @@ const struct IDTypeInfo *BKE_idtype_get_info_from_id(const struct ID *id);
|
||||
const char *BKE_idtype_idcode_to_name(const short idcode);
|
||||
const char *BKE_idtype_idcode_to_name_plural(const short idcode);
|
||||
const char *BKE_idtype_idcode_to_translation_context(const short idcode);
|
||||
bool BKE_idtype_idcode_is_linkable(const short idcode);
|
||||
|
||||
bool BKE_idtype_idcode_is_valid(const short idcode);
|
||||
|
||||
bool BKE_idtype_idcode_is_linkable(const short idcode);
|
||||
bool BKE_idtype_idcode_is_only_appendable(const short idcode);
|
||||
/* Macro currently, since any linkable IDtype should be localizable. */
|
||||
#define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable
|
||||
|
||||
short BKE_idtype_idcode_from_name(const char *idtype_name);
|
||||
|
||||
uint64_t BKE_idtype_idcode_to_idfilter(const short idcode);
|
||||
|
@@ -230,7 +230,7 @@ void id_us_plus(struct ID *id);
|
||||
void id_us_min(struct ID *id);
|
||||
void id_fake_user_set(struct ID *id);
|
||||
void id_fake_user_clear(struct ID *id);
|
||||
void BKE_id_clear_newpoin(struct ID *id);
|
||||
void BKE_id_newptr_and_tag_clear(struct ID *id);
|
||||
|
||||
/** Flags to control make local code behavior. */
|
||||
enum {
|
||||
@@ -248,7 +248,7 @@ enum {
|
||||
};
|
||||
|
||||
void BKE_lib_id_make_local_generic(struct Main *bmain, struct ID *id, const int flags);
|
||||
bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const bool test, const int flags);
|
||||
bool BKE_lib_id_make_local(struct Main *bmain, struct ID *id, const int flags);
|
||||
bool id_single_user(struct bContext *C,
|
||||
struct ID *id,
|
||||
struct PointerRNA *ptr,
|
||||
|
@@ -112,6 +112,7 @@ void BKE_libblock_relink_ex(struct Main *bmain,
|
||||
const short remap_flags) ATTR_NONNULL(1, 2);
|
||||
|
||||
void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL();
|
||||
void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL();
|
||||
|
||||
typedef void (*BKE_library_free_notifier_reference_cb)(const void *);
|
||||
typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *);
|
||||
|
@@ -123,7 +123,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval);
|
||||
|
||||
/* Performs copy for use during evaluation,
|
||||
* optional referencing original arrays to reduce memory. */
|
||||
struct Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference);
|
||||
struct Mesh *BKE_mesh_copy_for_eval(const struct Mesh *source, bool reference);
|
||||
|
||||
/* These functions construct a new Mesh,
|
||||
* contrary to BKE_mesh_to_curve_nurblist which modifies ob itself. */
|
||||
|
@@ -731,6 +731,8 @@ void nodeSetSocketAvailability(struct bNodeSocket *sock, bool is_available);
|
||||
|
||||
int nodeSocketLinkLimit(const struct bNodeSocket *sock);
|
||||
|
||||
void nodeDeclarationEnsure(struct bNodeTree *ntree, struct bNode *node);
|
||||
|
||||
/* Node Clipboard */
|
||||
void BKE_node_clipboard_init(const struct bNodeTree *ntree);
|
||||
void BKE_node_clipboard_clear(void);
|
||||
@@ -1489,6 +1491,9 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
|
||||
#define GEO_NODE_INPUT_INDEX 1078
|
||||
#define GEO_NODE_INPUT_NORMAL 1079
|
||||
#define GEO_NODE_ATTRIBUTE_CAPTURE 1080
|
||||
#define GEO_NODE_MATERIAL_SELECTION 1081
|
||||
#define GEO_NODE_MATERIAL_ASSIGN 1082
|
||||
#define GEO_NODE_CURVE_FILLET 1083
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@@ -77,7 +77,7 @@ char *BKE_packedfile_unpack_to_file(struct ReportList *reports,
|
||||
char *BKE_packedfile_unpack(struct Main *bmain,
|
||||
struct ReportList *reports,
|
||||
struct ID *id,
|
||||
const char *orig_file_name,
|
||||
const char *orig_file_path,
|
||||
struct PackedFile *pf,
|
||||
enum ePF_FileStatus how);
|
||||
int BKE_packedfile_unpack_vfont(struct Main *bmain,
|
||||
|
@@ -96,13 +96,24 @@ typedef struct SoundInfo {
|
||||
eSoundChannels channels;
|
||||
} specs;
|
||||
float length;
|
||||
double start_offset;
|
||||
} SoundInfo;
|
||||
|
||||
typedef struct SoundStreamInfo {
|
||||
double duration;
|
||||
double start;
|
||||
} SoundStreamInfo;
|
||||
|
||||
/* Get information about given sound. Returns truth on success., false if sound can not be loaded
|
||||
* or if the codes is not supported. */
|
||||
bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *sound_info);
|
||||
|
||||
/* Get information about given sound. Returns truth on success., false if sound can not be loaded
|
||||
* or if the codes is not supported. */
|
||||
bool BKE_sound_stream_info_get(struct Main *main,
|
||||
const char *filepath,
|
||||
int stream,
|
||||
SoundStreamInfo *sound_info);
|
||||
|
||||
#if defined(WITH_AUDASPACE)
|
||||
AUD_Device *BKE_sound_mixdown(const struct Scene *scene,
|
||||
AUD_DeviceSpecs specs,
|
||||
|
@@ -130,6 +130,11 @@ class Spline {
|
||||
virtual void translate(const blender::float3 &translation);
|
||||
virtual void transform(const blender::float4x4 &matrix);
|
||||
|
||||
/**
|
||||
* Change the direction of the spline (switch the start and end) without changing its shape.
|
||||
*/
|
||||
void reverse();
|
||||
|
||||
/**
|
||||
* Mark all caches for re-computation. This must be called after any operation that would
|
||||
* change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
|
||||
@@ -210,6 +215,7 @@ class Spline {
|
||||
virtual void correct_end_tangents() const = 0;
|
||||
virtual void copy_settings(Spline &dst) const = 0;
|
||||
virtual void copy_data(Spline &dst) const = 0;
|
||||
virtual void reverse_impl() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -353,6 +359,9 @@ class BezierSpline final : public Spline {
|
||||
void correct_end_tangents() const final;
|
||||
void copy_settings(Spline &dst) const final;
|
||||
void copy_data(Spline &dst) const final;
|
||||
|
||||
protected:
|
||||
void reverse_impl() override;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -469,6 +478,7 @@ class NURBSpline final : public Spline {
|
||||
void correct_end_tangents() const final;
|
||||
void copy_settings(Spline &dst) const final;
|
||||
void copy_data(Spline &dst) const final;
|
||||
void reverse_impl() override;
|
||||
|
||||
void calculate_knots() const;
|
||||
blender::Span<BasisCache> calculate_basis_cache() const;
|
||||
@@ -519,6 +529,7 @@ class PolySpline final : public Spline {
|
||||
void correct_end_tangents() const final;
|
||||
void copy_settings(Spline &dst) const final;
|
||||
void copy_data(Spline &dst) const final;
|
||||
void reverse_impl() override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -186,7 +186,7 @@ set(SRC
|
||||
intern/mball_tessellate.c
|
||||
intern/mesh.c
|
||||
intern/mesh_boolean_convert.cc
|
||||
intern/mesh_convert.c
|
||||
intern/mesh_convert.cc
|
||||
intern/mesh_evaluate.cc
|
||||
intern/mesh_fair.cc
|
||||
intern/mesh_iterators.c
|
||||
|
@@ -190,7 +190,7 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains)
|
||||
void OutputAttribute::save()
|
||||
{
|
||||
save_has_been_called_ = true;
|
||||
if (optional_span_varray_.has_value()) {
|
||||
if (optional_span_varray_) {
|
||||
optional_span_varray_->save();
|
||||
}
|
||||
if (save_) {
|
||||
@@ -817,6 +817,12 @@ bool GeometryComponent::attribute_is_builtin(const blender::StringRef attribute_
|
||||
return providers->builtin_attribute_providers().contains_as(attribute_name);
|
||||
}
|
||||
|
||||
bool GeometryComponent::attribute_is_builtin(const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
/* Anonymous attributes cannot be built-in. */
|
||||
return attribute_id.is_named() && this->attribute_is_builtin(attribute_id.name());
|
||||
}
|
||||
|
||||
blender::bke::ReadAttributeLookup GeometryComponent::attribute_try_get_for_read(
|
||||
const AttributeIDRef &attribute_id) const
|
||||
{
|
||||
@@ -1210,7 +1216,7 @@ static OutputAttribute create_output_attribute(GeometryComponent &component,
|
||||
BLI_assert(cpp_type != nullptr);
|
||||
const nodes::DataTypeConversions &conversions = nodes::get_implicit_type_conversions();
|
||||
|
||||
if (attribute_id.is_named() && component.attribute_is_builtin(attribute_id.name())) {
|
||||
if (component.attribute_is_builtin(attribute_id)) {
|
||||
const StringRef attribute_name = attribute_id.name();
|
||||
WriteAttributeLookup attribute = component.attribute_try_get_for_write(attribute_name);
|
||||
if (!attribute) {
|
||||
@@ -1315,7 +1321,9 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex
|
||||
const AttributeDomain domain = geometry_context->domain();
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
|
||||
GVArrayPtr attribute = component.attribute_try_get_for_read(name_, domain, data_type);
|
||||
return scope.add(std::move(attribute), __func__);
|
||||
if (attribute) {
|
||||
return scope.add(std::move(attribute));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1350,7 +1358,7 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context(
|
||||
const CustomDataType data_type = cpp_type_to_custom_data_type(*type_);
|
||||
GVArrayPtr attribute = component.attribute_try_get_for_read(
|
||||
anonymous_id_.get(), domain, data_type);
|
||||
return scope.add(std::move(attribute), __func__);
|
||||
return scope.add(std::move(attribute));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
@@ -161,7 +161,13 @@ static void brush_make_local(Main *bmain, ID *id, const int flags)
|
||||
|
||||
if (brush->clone.image) {
|
||||
/* Special case: ima always local immediately. Clone image should only have one user anyway. */
|
||||
BKE_lib_id_make_local(bmain, &brush->clone.image->id, false, 0);
|
||||
/* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided
|
||||
* in IDType callbacks. Higher-level ID management code usually does not expect such things and
|
||||
* does not deal properly with it. */
|
||||
/* NOTE: assert below ensures that the comment above is valid, and that that exception is
|
||||
* acceptable for the time being. */
|
||||
BKE_lib_id_make_local(bmain, &brush->clone.image->id, 0);
|
||||
BLI_assert(brush->clone.image->id.lib == NULL && brush->clone.image->id.newid == NULL);
|
||||
}
|
||||
|
||||
if (!force_local && !force_copy) {
|
||||
|
@@ -110,7 +110,7 @@ void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluate
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start indices for each of the curve spline's evaluated points, as if they were part
|
||||
* Return the start indices for each of the curve spline's control points, if they were part
|
||||
* of a flattened array. This can be used to facilitate parallelism by avoiding the need to
|
||||
* accumulate an offset while doing more complex calculations.
|
||||
*
|
||||
|
@@ -1504,7 +1504,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph,
|
||||
cow_curve.curve_eval = nullptr;
|
||||
|
||||
ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__);
|
||||
ListBase *dispbase = &(ob->runtime.curve_cache->disp);
|
||||
ListBase *dispbase = &ob->runtime.curve_cache->disp;
|
||||
|
||||
if (ob->type == OB_SURF) {
|
||||
Mesh *mesh_eval;
|
||||
|
@@ -2064,7 +2064,7 @@ static Mesh *dynamicPaint_Modifier_apply(DynamicPaintModifierData *pmd, Object *
|
||||
}
|
||||
|
||||
if (update_normals) {
|
||||
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
BKE_mesh_normals_tag_dirty(result);
|
||||
}
|
||||
}
|
||||
/* make a copy of mesh to use as brush data */
|
||||
|
@@ -3573,7 +3573,7 @@ static Mesh *create_smoke_geometry(FluidDomainSettings *fds, Mesh *orgmesh, Obje
|
||||
}
|
||||
|
||||
BKE_mesh_calc_edges(result, false, false);
|
||||
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
BKE_mesh_normals_tag_dirty(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@@ -1419,17 +1419,19 @@ static float eval_fmodifier_influence(FModifier *fcm, float evaltime)
|
||||
|
||||
/* restricted range or full range? */
|
||||
if (fcm->flag & FMODIFIER_FLAG_RANGERESTRICT) {
|
||||
if ((evaltime <= fcm->sfra) || (evaltime >= fcm->efra)) {
|
||||
if ((evaltime < fcm->sfra) || (evaltime > fcm->efra)) {
|
||||
/* out of range */
|
||||
return 0.0f;
|
||||
}
|
||||
if ((evaltime > fcm->sfra) && (evaltime < fcm->sfra + fcm->blendin)) {
|
||||
if ((fcm->blendin != 0.0f) && (evaltime >= fcm->sfra) &&
|
||||
(evaltime <= fcm->sfra + fcm->blendin)) {
|
||||
/* blend in range */
|
||||
float a = fcm->sfra;
|
||||
float b = fcm->sfra + fcm->blendin;
|
||||
return influence * (evaltime - a) / (b - a);
|
||||
}
|
||||
if ((evaltime < fcm->efra) && (evaltime > fcm->efra - fcm->blendout)) {
|
||||
if ((fcm->blendout != 0.0f) && (evaltime <= fcm->efra) &&
|
||||
(evaltime >= fcm->efra - fcm->blendout)) {
|
||||
/* blend out range */
|
||||
float a = fcm->efra;
|
||||
float b = fcm->efra - fcm->blendout;
|
||||
|
@@ -719,6 +719,9 @@ typedef struct VFontToCurveIter {
|
||||
*
|
||||
* Currently only disabled when scale-to-fit is enabled,
|
||||
* so floating-point error doesn't cause unexpected wrapping, see T89241.
|
||||
*
|
||||
* \note This should only be set once, in the #VFONT_TO_CURVE_INIT pass
|
||||
* otherwise iterations wont behave predictably, see T91401.
|
||||
*/
|
||||
bool word_wrap;
|
||||
int status;
|
||||
@@ -750,8 +753,15 @@ enum {
|
||||
*
|
||||
* The em_height here is relative to FT_Face->bbox.
|
||||
*/
|
||||
#define ASCENT(vfd) ((vfd)->ascender * (vfd)->em_height)
|
||||
#define DESCENT(vfd) ((vfd)->em_height - ASCENT(vfd))
|
||||
|
||||
static float vfont_ascent(const VFontData *vfd)
|
||||
{
|
||||
return vfd->ascender * vfd->em_height;
|
||||
}
|
||||
static float vfont_descent(const VFontData *vfd)
|
||||
{
|
||||
return vfd->em_height - vfont_ascent(vfd);
|
||||
}
|
||||
|
||||
static bool vfont_to_curve(Object *ob,
|
||||
Curve *cu,
|
||||
@@ -1234,17 +1244,17 @@ static bool vfont_to_curve(Object *ob,
|
||||
case CU_ALIGN_Y_TOP_BASELINE:
|
||||
break;
|
||||
case CU_ALIGN_Y_TOP:
|
||||
yoff = textbox_y_origin - ASCENT(vfd);
|
||||
yoff = textbox_y_origin - vfont_ascent(vfd);
|
||||
break;
|
||||
case CU_ALIGN_Y_CENTER:
|
||||
yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - ASCENT(vfd)) -
|
||||
yoff = ((((vfd->em_height + (lines - 1) * linedist) * 0.5f) - vfont_ascent(vfd)) -
|
||||
(tb_scale.h * 0.5f) + textbox_y_origin);
|
||||
break;
|
||||
case CU_ALIGN_Y_BOTTOM_BASELINE:
|
||||
yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h;
|
||||
break;
|
||||
case CU_ALIGN_Y_BOTTOM:
|
||||
yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + DESCENT(vfd);
|
||||
yoff = textbox_y_origin + ((lines - 1) * linedist) - tb_scale.h + vfont_descent(vfd);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1265,16 +1275,16 @@ static bool vfont_to_curve(Object *ob,
|
||||
case CU_ALIGN_Y_TOP_BASELINE:
|
||||
break;
|
||||
case CU_ALIGN_Y_TOP:
|
||||
yoff = -ASCENT(vfd);
|
||||
yoff = -vfont_ascent(vfd);
|
||||
break;
|
||||
case CU_ALIGN_Y_CENTER:
|
||||
yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - ASCENT(vfd);
|
||||
yoff = ((vfd->em_height + (lnr - 1) * linedist) * 0.5f) - vfont_ascent(vfd);
|
||||
break;
|
||||
case CU_ALIGN_Y_BOTTOM_BASELINE:
|
||||
yoff = (lnr - 1) * linedist;
|
||||
break;
|
||||
case CU_ALIGN_Y_BOTTOM:
|
||||
yoff = (lnr - 1) * linedist + DESCENT(vfd);
|
||||
yoff = (lnr - 1) * linedist + vfont_descent(vfd);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1640,7 +1650,6 @@ static bool vfont_to_curve(Object *ob,
|
||||
else {
|
||||
iter_data->scale_to_fit = iter_data->bisect.min;
|
||||
iter_data->status = VFONT_TO_CURVE_SCALE_ONCE;
|
||||
iter_data->word_wrap = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -222,6 +222,37 @@ static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/**
|
||||
* A spline is selected if all of its control points were selected.
|
||||
*
|
||||
* \note Theoretically this interpolation does not need to compute all values at once.
|
||||
* However, doing that makes the implementation simpler, and this can be optimized in the future if
|
||||
* only some values are required.
|
||||
*/
|
||||
template<>
|
||||
void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
const int splines_len = curve.splines().size();
|
||||
Array<int> offsets = curve.control_point_offsets();
|
||||
BLI_assert(r_values.size() == splines_len);
|
||||
|
||||
r_values.fill(true);
|
||||
|
||||
for (const int i_spline : IndexRange(splines_len)) {
|
||||
const int spline_offset = offsets[i_spline];
|
||||
const int spline_point_len = offsets[i_spline + 1] - spline_offset;
|
||||
|
||||
for (const int i_point : IndexRange(spline_point_len)) {
|
||||
if (!old_values[spline_offset + i_point]) {
|
||||
r_values[i_spline] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
|
@@ -175,6 +175,34 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* A vertex is selected if all connected face corners were selected and it is not loose. */
|
||||
template<>
|
||||
void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
Array<bool> loose_verts(mesh.totvert, true);
|
||||
|
||||
r_values.fill(true);
|
||||
for (const int loop_index : IndexRange(mesh.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const int point_index = loop.v;
|
||||
|
||||
loose_verts[point_index] = false;
|
||||
if (!old_values[loop_index]) {
|
||||
r_values[point_index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Deselect loose vertices without corners that are still selected from the 'true' default. */
|
||||
for (const int vert_index : IndexRange(mesh.totvert)) {
|
||||
if (loose_verts[vert_index]) {
|
||||
r_values[vert_index] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -191,6 +219,13 @@ static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Each corner's value is simply a copy of the value at its vertex.
|
||||
*
|
||||
* \note Theoretically this interpolation does not need to compute all values at once.
|
||||
* However, doing that makes the implementation simpler, and this can be optimized in the future if
|
||||
* only some values are required.
|
||||
*/
|
||||
template<typename T>
|
||||
static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh,
|
||||
const VArray<T> &old_values,
|
||||
@@ -209,10 +244,6 @@ static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr
|
||||
GVArrayPtr new_varray;
|
||||
attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
/* It is not strictly necessary to compute the value for all corners here. Instead one could
|
||||
* lazily lookup the mesh topology when a specific index accessed. This can be more efficient
|
||||
* when an algorithm only accesses very few of the corner values. However, for the algorithms
|
||||
* we currently have, precomputing the array is fine. Also, it is easier to implement. */
|
||||
Array<T> values(mesh.totloop);
|
||||
adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values);
|
||||
new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values));
|
||||
@@ -244,6 +275,26 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* A face is selected if all of its corners were selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
|
||||
r_values.fill(true);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
if (!old_values[loop_index]) {
|
||||
r_values[poly_index] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -282,6 +333,41 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* An edge is selected if all corners on adjacent faces were selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
|
||||
/* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */
|
||||
Array<bool> loose_edges(mesh.totedge, true);
|
||||
|
||||
r_values.fill(true);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1);
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const int edge_index = loop.e;
|
||||
loose_edges[edge_index] = false;
|
||||
|
||||
if (!old_values[loop_index] || !old_values[loop_index_next]) {
|
||||
r_values[edge_index] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Deselect loose edges without corners that are still selected from the 'true' default. */
|
||||
for (const int edge_index : IndexRange(mesh.totedge)) {
|
||||
if (loose_edges[edge_index]) {
|
||||
r_values[edge_index] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -317,6 +403,27 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* A vertex is selected if any of the connected faces were selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
|
||||
r_values.fill(false);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
if (old_values[poly_index]) {
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const int vert_index = loop.v;
|
||||
r_values[vert_index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -331,6 +438,7 @@ static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr v
|
||||
return new_varray;
|
||||
}
|
||||
|
||||
/* Each corner's value is simply a copy of the value at its face. */
|
||||
template<typename T>
|
||||
void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh,
|
||||
const VArray<T> &old_values,
|
||||
@@ -378,6 +486,27 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* An edge is selected if any connected face was selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
|
||||
r_values.fill(false);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
if (old_values[poly_index]) {
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const int edge_index = loop.e;
|
||||
r_values[edge_index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -416,6 +545,28 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* A face is selected if all of its vertices were selected too. */
|
||||
template<>
|
||||
void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
|
||||
r_values.fill(true);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
MLoop &loop = mesh.mloop[loop_index];
|
||||
const int vert_index = loop.v;
|
||||
if (!old_values[vert_index]) {
|
||||
r_values[poly_index] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -452,6 +603,20 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* An edge is selected if both of its vertices were selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totedge);
|
||||
|
||||
for (const int edge_index : IndexRange(mesh.totedge)) {
|
||||
const MEdge &edge = mesh.medge[edge_index];
|
||||
r_values[edge_index] = old_values[edge.v1] && old_values[edge.v2];
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -490,6 +655,29 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* A corner is selected if its two adjacent edges were selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totloop);
|
||||
|
||||
r_values.fill(false);
|
||||
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop;
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const MLoop &loop_prev = mesh.mloop[loop_index_prev];
|
||||
if (old_values[loop.e] && old_values[loop_prev.e]) {
|
||||
r_values[loop_index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -522,6 +710,24 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* A vertex is selected if any connected edge was selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totvert);
|
||||
|
||||
r_values.fill(false);
|
||||
for (const int edge_index : IndexRange(mesh.totedge)) {
|
||||
const MEdge &edge = mesh.medge[edge_index];
|
||||
if (old_values[edge_index]) {
|
||||
r_values[edge.v1] = true;
|
||||
r_values[edge.v2] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -560,6 +766,28 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
|
||||
mixer.finalize();
|
||||
}
|
||||
|
||||
/* A face is selected if all of its edges are selected. */
|
||||
template<>
|
||||
void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh,
|
||||
const VArray<bool> &old_values,
|
||||
MutableSpan<bool> r_values)
|
||||
{
|
||||
BLI_assert(r_values.size() == mesh.totpoly);
|
||||
|
||||
r_values.fill(true);
|
||||
for (const int poly_index : IndexRange(mesh.totpoly)) {
|
||||
const MPoly &poly = mesh.mpoly[poly_index];
|
||||
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
|
||||
const MLoop &loop = mesh.mloop[loop_index];
|
||||
const int edge_index = loop.e;
|
||||
if (!old_values[edge_index]) {
|
||||
r_values[poly_index] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray)
|
||||
{
|
||||
GVArrayPtr new_varray;
|
||||
@@ -698,7 +926,7 @@ static void tag_normals_dirty_when_writing_position(GeometryComponent &component
|
||||
{
|
||||
Mesh *mesh = get_mesh_from_component_for_write(component);
|
||||
if (mesh != nullptr) {
|
||||
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3063,13 +3063,12 @@ void BKE_gpencil_update_layer_transforms(const Depsgraph *depsgraph, Object *ob)
|
||||
Object *ob_parent = DEG_get_evaluated_object(depsgraph, gpl->parent);
|
||||
/* calculate new matrix */
|
||||
if (ELEM(gpl->partype, PAROBJECT, PARSKEL)) {
|
||||
copy_m4_m4(cur_mat, ob_parent->obmat);
|
||||
mul_m4_m4m4(cur_mat, ob->imat, ob_parent->obmat);
|
||||
}
|
||||
else if (gpl->partype == PARBONE) {
|
||||
bPoseChannel *pchan = BKE_pose_channel_find_name(ob_parent->pose, gpl->parsubstr);
|
||||
if (pchan != NULL) {
|
||||
copy_m4_m4(cur_mat, ob->imat);
|
||||
mul_m4_m4m4(cur_mat, ob_parent->obmat, pchan->pose_mat);
|
||||
mul_m4_series(cur_mat, ob->imat, ob_parent->obmat, pchan->pose_mat);
|
||||
}
|
||||
else {
|
||||
unit_m4(cur_mat);
|
||||
|
@@ -938,6 +938,11 @@ void BKE_gpencil_modifier_blend_write(BlendWriter *writer, ListBase *modbase)
|
||||
BKE_curvemapping_blend_write(writer, gpmd->curve_intensity);
|
||||
}
|
||||
}
|
||||
else if (md->type == eGpencilModifierType_Dash) {
|
||||
DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md;
|
||||
BLO_write_struct_array(
|
||||
writer, DashGpencilModifierSegment, gpmd->segments_len, gpmd->segments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,6 +1022,13 @@ void BKE_gpencil_modifier_blend_read_data(BlendDataReader *reader, ListBase *lb)
|
||||
BKE_curvemapping_init(gpmd->curve_intensity);
|
||||
}
|
||||
}
|
||||
else if (md->type == eGpencilModifierType_Dash) {
|
||||
DashGpencilModifierData *gpmd = (DashGpencilModifierData *)md;
|
||||
BLO_read_data_address(reader, &gpmd->segments);
|
||||
for (int i = 0; i < gpmd->segments_len; i++) {
|
||||
gpmd->segments[i].dmd = gpmd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -633,12 +633,6 @@ void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv)
|
||||
}
|
||||
|
||||
PreviewImage prv_copy = *prv;
|
||||
/* don't write out large previews if not requested */
|
||||
if (U.file_preview_type == USER_FILE_PREVIEW_NONE) {
|
||||
prv_copy.w[1] = 0;
|
||||
prv_copy.h[1] = 0;
|
||||
prv_copy.rect[1] = nullptr;
|
||||
}
|
||||
BLO_write_struct_at_address(writer, PreviewImage, prv, &prv_copy);
|
||||
if (prv_copy.rect[0]) {
|
||||
BLO_write_uint32_array(writer, prv_copy.w[0] * prv_copy.h[0], prv_copy.rect[0]);
|
||||
|
@@ -224,10 +224,10 @@ bool BKE_idtype_idcode_is_valid(const short idcode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return non-zero when an ID type is linkable.
|
||||
* Check if an ID type is linkable.
|
||||
*
|
||||
* \param idcode: The code to check.
|
||||
* \return Boolean, 0 when non linkable.
|
||||
* \param idcode: The IDType code to check.
|
||||
* \return Boolean, false when non linkable, true otherwise.
|
||||
*/
|
||||
bool BKE_idtype_idcode_is_linkable(const short idcode)
|
||||
{
|
||||
@@ -236,6 +236,24 @@ bool BKE_idtype_idcode_is_linkable(const short idcode)
|
||||
return id_type != NULL ? (id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0 : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an ID type is only appendable.
|
||||
*
|
||||
* \param idcode: The IDType code to check.
|
||||
* \return Boolean, false when also linkable, true when only appendable.
|
||||
*/
|
||||
bool BKE_idtype_idcode_is_only_appendable(const short idcode)
|
||||
{
|
||||
const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode);
|
||||
BLI_assert(id_type != NULL);
|
||||
if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_ONLY_APPEND) != 0) {
|
||||
/* Only appendable ID types should also always be linkable. */
|
||||
BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB).
|
||||
*/
|
||||
|
@@ -184,8 +184,7 @@ IDTypeInfo IDType_ID_IP = {
|
||||
.name = "Ipo",
|
||||
.name_plural = "ipos",
|
||||
.translation_context = "",
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL |
|
||||
IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
|
||||
.init_data = NULL,
|
||||
.copy_data = NULL,
|
||||
|
@@ -212,7 +212,7 @@ IDTypeInfo IDType_ID_KE = {
|
||||
.name = "Key",
|
||||
.name_plural = "shape_keys",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_SHAPEKEY,
|
||||
.flags = IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL,
|
||||
.flags = IDTYPE_FLAGS_NO_LIBLINKING,
|
||||
|
||||
.init_data = NULL,
|
||||
.copy_data = shapekey_copy_data,
|
||||
|
@@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_LINK_PLACEHOLDER = {
|
||||
.name = "LinkPlaceholder",
|
||||
.name_plural = "link_placeholders",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_ID,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING,
|
||||
|
||||
.init_data = NULL,
|
||||
.copy_data = NULL,
|
||||
@@ -336,12 +336,34 @@ void id_fake_user_clear(ID *id)
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_id_clear_newpoin(ID *id)
|
||||
void BKE_id_newptr_and_tag_clear(ID *id)
|
||||
{
|
||||
if (id->newid) {
|
||||
id->newid->tag &= ~LIB_TAG_NEW;
|
||||
/* We assume that if this ID has no new ID, its embedded data has not either. */
|
||||
if (id->newid == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
id->newid->tag &= ~LIB_TAG_NEW;
|
||||
id->newid = NULL;
|
||||
|
||||
/* Deal with embedded data too. */
|
||||
/* NOTE: even though ShapeKeys are not technically embedded data currently, they behave as such
|
||||
* in most cases, so for sake of consistency treat them as such here. Also mirrors the behavior
|
||||
* in `BKE_lib_id_make_local`. */
|
||||
Key *key = BKE_key_from_id(id);
|
||||
if (key != NULL) {
|
||||
BKE_id_newptr_and_tag_clear(&key->id);
|
||||
}
|
||||
bNodeTree *ntree = ntreeFromID(id);
|
||||
if (ntree != NULL) {
|
||||
BKE_id_newptr_and_tag_clear(&ntree->id);
|
||||
}
|
||||
if (GS(id->name) == ID_SCE) {
|
||||
Collection *master_collection = ((Scene *)id)->master_collection;
|
||||
if (master_collection != NULL) {
|
||||
BKE_id_newptr_and_tag_clear(&master_collection->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data)
|
||||
@@ -480,10 +502,9 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags)
|
||||
*
|
||||
* \param flags: Special flag used when making a whole library's content local,
|
||||
* it needs specific handling.
|
||||
*
|
||||
* \return true if the block can be made local.
|
||||
* \return true is the ID has successfully been made local.
|
||||
*/
|
||||
bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags)
|
||||
bool BKE_lib_id_make_local(Main *bmain, ID *id, const int flags)
|
||||
{
|
||||
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
|
||||
|
||||
@@ -495,23 +516,21 @@ bool BKE_lib_id_make_local(Main *bmain, ID *id, const bool test, const int flags
|
||||
|
||||
const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id);
|
||||
|
||||
if (idtype_info != NULL) {
|
||||
if ((idtype_info->flags & IDTYPE_FLAGS_NO_MAKELOCAL) == 0) {
|
||||
if (!test) {
|
||||
if (idtype_info->make_local != NULL) {
|
||||
idtype_info->make_local(bmain, id, flags);
|
||||
}
|
||||
else {
|
||||
BKE_lib_id_make_local_generic(bmain, id, flags);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (idtype_info == NULL) {
|
||||
BLI_assert_msg(0, "IDType Missing IDTypeInfo");
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_assert_msg(0, "IDType Missing IDTypeInfo");
|
||||
return false;
|
||||
BLI_assert((idtype_info->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0);
|
||||
|
||||
if (idtype_info->make_local != NULL) {
|
||||
idtype_info->make_local(bmain, id, flags);
|
||||
}
|
||||
else {
|
||||
BKE_lib_id_make_local_generic(bmain, id, flags);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct IDCopyLibManagementData {
|
||||
@@ -1706,7 +1725,7 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
|
||||
*
|
||||
* Only for local IDs (linked ones already have a unique ID in their library).
|
||||
*
|
||||
* \param do_linked_data if true, also ensure a unique name in case the given \a id is linked
|
||||
* \param do_linked_data: if true, also ensure a unique name in case the given \a id is linked
|
||||
* (otherwise, just ensure that it is properly sorted).
|
||||
*
|
||||
* \return true if a new name had to be created.
|
||||
@@ -1766,8 +1785,7 @@ void BKE_main_id_newptr_and_tag_clear(Main *bmain)
|
||||
ID *id;
|
||||
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
id->newid = NULL;
|
||||
id->tag &= ~LIB_TAG_NEW;
|
||||
BKE_id_newptr_and_tag_clear(id);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
}
|
||||
@@ -2034,11 +2052,8 @@ void BKE_library_make_local(Main *bmain,
|
||||
* Note that for objects, we don't want proxy pointers to be cleared yet. This will happen
|
||||
* down the road in this function.
|
||||
*/
|
||||
BKE_lib_id_make_local(bmain,
|
||||
id,
|
||||
false,
|
||||
LIB_ID_MAKELOCAL_FULL_LIBRARY |
|
||||
LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
|
||||
BKE_lib_id_make_local(
|
||||
bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
|
||||
|
||||
if (id->newid) {
|
||||
if (GS(id->newid->name) == ID_OB) {
|
||||
|
@@ -333,11 +333,11 @@ ID *BKE_lib_override_library_create_from_id(Main *bmain,
|
||||
* main. You can add more local IDs to be remapped to use new overriding ones by setting their
|
||||
* LIB_TAG_DOIT tag.
|
||||
*
|
||||
* \param reference_library the library from which the linked data being overridden come from
|
||||
* \param reference_library: the library from which the linked data being overridden come from
|
||||
* (i.e. the library of the linked reference ID).
|
||||
*
|
||||
* \param do_no_main Create the new override data outside of Main database. Used for resyncing of
|
||||
* linked overrides.
|
||||
* \param do_no_main: Create the new override data outside of Main database.
|
||||
* Used for resyncing of linked overrides.
|
||||
*
|
||||
* \return \a true on success, \a false otherwise.
|
||||
*/
|
||||
@@ -901,7 +901,7 @@ static void lib_override_library_create_post_process(Main *bmain,
|
||||
* \param id_reference: Some reference ID used to do some post-processing after overrides have been
|
||||
* created, may be NULL. Typically, the Empty object instantiating the linked collection we
|
||||
* override, currently.
|
||||
* \param r_id_root_override if not NULL, the override generated for the given \a id_root.
|
||||
* \param r_id_root_override: if not NULL, the override generated for the given \a id_root.
|
||||
* \return true if override was successfully created.
|
||||
*/
|
||||
bool BKE_lib_override_library_create(Main *bmain,
|
||||
|
@@ -720,9 +720,9 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain,
|
||||
* Valid usages here are defined as ref-counting usages, which are not towards embedded or
|
||||
* loop-back data.
|
||||
*
|
||||
* \param r_num_tagged If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers.
|
||||
* Number of tagged-as-unused IDs is then set for each type, and as total in
|
||||
* #INDEX_ID_NULL item.
|
||||
* \param r_num_tagged: If non-NULL, must be a zero-initialized array of #INDEX_ID_MAX integers.
|
||||
* Number of tagged-as-unused IDs is then set for each type, and as total in
|
||||
* #INDEX_ID_NULL item.
|
||||
*/
|
||||
void BKE_lib_query_unused_ids_tag(Main *bmain,
|
||||
const int tag,
|
||||
|
@@ -699,6 +699,9 @@ static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data)
|
||||
*
|
||||
* Very specific usage, not sure we'll keep it on the long run,
|
||||
* currently only used in Object/Collection duplication code...
|
||||
*
|
||||
* WARNING: This is a deprecated version of this function, should not be used by new code. See
|
||||
* #BKE_libblock_relink_to_newid_new below.
|
||||
*/
|
||||
void BKE_libblock_relink_to_newid(ID *id)
|
||||
{
|
||||
@@ -708,3 +711,53 @@ void BKE_libblock_relink_to_newid(ID *id)
|
||||
|
||||
BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0);
|
||||
}
|
||||
|
||||
/* ************************
|
||||
* FIXME: Port all usages of #BKE_libblock_relink_to_newid to this
|
||||
* #BKE_libblock_relink_to_newid_new new code and remove old one.
|
||||
************************** */
|
||||
static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data)
|
||||
{
|
||||
const int cb_flag = cb_data->cb_flag;
|
||||
if (cb_flag & IDWALK_CB_EMBEDDED) {
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
Main *bmain = cb_data->bmain;
|
||||
ID *id_owner = cb_data->id_owner;
|
||||
ID **id_pointer = cb_data->id_pointer;
|
||||
ID *id = *id_pointer;
|
||||
if (id) {
|
||||
/* See: NEW_ID macro */
|
||||
if (id->newid != NULL) {
|
||||
BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE);
|
||||
id = id->newid;
|
||||
}
|
||||
if (id->tag & LIB_TAG_NEW) {
|
||||
id->tag &= ~LIB_TAG_NEW;
|
||||
BKE_libblock_relink_to_newid_new(bmain, id);
|
||||
}
|
||||
}
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively
|
||||
* in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`.
|
||||
*
|
||||
* NOTE: `LIB_TAG_NEW` is cleared
|
||||
*
|
||||
* Very specific usage, not sure we'll keep it on the long run,
|
||||
* currently only used in Object/Collection duplication code...
|
||||
*/
|
||||
void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id)
|
||||
{
|
||||
if (ID_IS_LINKED(id)) {
|
||||
return;
|
||||
}
|
||||
/* We do not want to have those cached relationship data here. */
|
||||
BLI_assert(bmain->relations == NULL);
|
||||
|
||||
id->tag &= ~LIB_TAG_NEW;
|
||||
BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0);
|
||||
}
|
||||
|
@@ -68,8 +68,7 @@ IDTypeInfo IDType_ID_LI = {
|
||||
.name = "Library",
|
||||
.name_plural = "libraries",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_LIBRARY,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_MAKELOCAL |
|
||||
IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
|
||||
.init_data = NULL,
|
||||
.copy_data = NULL,
|
||||
|
@@ -1111,7 +1111,7 @@ void BKE_mesh_eval_delete(struct Mesh *mesh_eval)
|
||||
MEM_freeN(mesh_eval);
|
||||
}
|
||||
|
||||
Mesh *BKE_mesh_copy_for_eval(struct Mesh *source, bool reference)
|
||||
Mesh *BKE_mesh_copy_for_eval(const Mesh *source, bool reference)
|
||||
{
|
||||
int flags = LIB_ID_COPY_LOCALIZE;
|
||||
|
||||
@@ -1859,7 +1859,7 @@ void BKE_mesh_vert_coords_apply(Mesh *mesh, const float (*vert_coords)[3])
|
||||
for (int i = 0; i < mesh->totvert; i++, mv++) {
|
||||
copy_v3_v3(mv->co, vert_coords[i]);
|
||||
}
|
||||
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
}
|
||||
|
||||
void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh,
|
||||
@@ -1872,7 +1872,7 @@ void BKE_mesh_vert_coords_apply_with_mat4(Mesh *mesh,
|
||||
for (int i = 0; i < mesh->totvert; i++, mv++) {
|
||||
mul_v3_m4v3(mv->co, mat, vert_coords[i]);
|
||||
}
|
||||
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
}
|
||||
|
||||
void BKE_mesh_vert_normals_apply(Mesh *mesh, const short (*vert_normals)[3])
|
||||
|
@@ -68,7 +68,7 @@
|
||||
|
||||
#ifdef VALIDATE_MESH
|
||||
# define ASSERT_IS_VALID_MESH(mesh) \
|
||||
(BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true)))
|
||||
(BLI_assert((mesh == nullptr) || (BKE_mesh_is_valid(mesh) == true)))
|
||||
#else
|
||||
# define ASSERT_IS_VALID_MESH(mesh)
|
||||
#endif
|
||||
@@ -84,15 +84,16 @@ void BKE_mesh_from_metaball(ListBase *lb, Mesh *me)
|
||||
const float *nors, *verts;
|
||||
int a, *index;
|
||||
|
||||
dl = lb->first;
|
||||
if (dl == NULL) {
|
||||
dl = (DispList *)lb->first;
|
||||
if (dl == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dl->type == DL_INDEX4) {
|
||||
mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, dl->nr);
|
||||
allloop = mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, dl->parts * 4);
|
||||
mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, dl->parts);
|
||||
mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, dl->nr);
|
||||
allloop = mloop = (MLoop *)CustomData_add_layer(
|
||||
&me->ldata, CD_MLOOP, CD_CALLOC, nullptr, dl->parts * 4);
|
||||
mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, nullptr, dl->parts);
|
||||
me->mvert = mvert;
|
||||
me->mloop = mloop;
|
||||
me->mpoly = mpoly;
|
||||
@@ -177,9 +178,10 @@ static void make_edges_mdata_extend(
|
||||
MEdge *medge;
|
||||
uint e_index = totedge;
|
||||
|
||||
*r_alledge = medge = (*r_alledge ?
|
||||
MEM_reallocN(*r_alledge, sizeof(MEdge) * (totedge + totedge_new)) :
|
||||
MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__));
|
||||
*r_alledge = medge = (MEdge *)(*r_alledge ?
|
||||
MEM_reallocN(*r_alledge,
|
||||
sizeof(MEdge) * (totedge + totedge_new)) :
|
||||
MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__));
|
||||
medge += totedge;
|
||||
|
||||
totedge += totedge_new;
|
||||
@@ -209,7 +211,7 @@ static void make_edges_mdata_extend(
|
||||
}
|
||||
}
|
||||
|
||||
BLI_edgehash_free(eh, NULL);
|
||||
BLI_edgehash_free(eh, nullptr);
|
||||
}
|
||||
|
||||
/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */
|
||||
@@ -229,7 +231,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
|
||||
MVert *mvert;
|
||||
MPoly *mpoly;
|
||||
MLoop *mloop;
|
||||
MLoopUV *mloopuv = NULL;
|
||||
MLoopUV *mloopuv = nullptr;
|
||||
MEdge *medge;
|
||||
const float *data;
|
||||
int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0;
|
||||
@@ -277,14 +279,15 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu,
|
||||
return -1;
|
||||
}
|
||||
|
||||
*r_allvert = mvert = MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert");
|
||||
*r_alledge = medge = MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge");
|
||||
*r_allloop = mloop = MEM_calloc_arrayN(
|
||||
*r_allvert = mvert = (MVert *)MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert");
|
||||
*r_alledge = medge = (MEdge *)MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge");
|
||||
*r_allloop = mloop = (MLoop *)MEM_calloc_arrayN(
|
||||
totpoly, sizeof(MLoop[4]), "nurbs_init mloop"); /* totloop */
|
||||
*r_allpoly = mpoly = MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop");
|
||||
*r_allpoly = mpoly = (MPoly *)MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop");
|
||||
|
||||
if (r_alluv) {
|
||||
*r_alluv = mloopuv = MEM_calloc_arrayN(totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv");
|
||||
*r_alluv = mloopuv = (MLoopUV *)MEM_calloc_arrayN(
|
||||
totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv");
|
||||
}
|
||||
|
||||
/* verts and faces */
|
||||
@@ -500,13 +503,13 @@ static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me)
|
||||
|
||||
Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase)
|
||||
{
|
||||
const Curve *cu = ob->data;
|
||||
const Curve *cu = (const Curve *)ob->data;
|
||||
Mesh *mesh;
|
||||
MVert *allvert;
|
||||
MEdge *alledge;
|
||||
MLoop *allloop;
|
||||
MPoly *allpoly;
|
||||
MLoopUV *alluv = NULL;
|
||||
MLoopUV *alluv = nullptr;
|
||||
int totvert, totedge, totloop, totpoly;
|
||||
|
||||
if (mesh_nurbs_displist_to_mdata(cu,
|
||||
@@ -525,7 +528,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *
|
||||
}
|
||||
|
||||
mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly);
|
||||
mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
BKE_mesh_normals_tag_dirty(mesh);
|
||||
|
||||
if (totvert != 0) {
|
||||
memcpy(mesh->mvert, allvert, totvert * sizeof(MVert));
|
||||
@@ -561,7 +564,7 @@ Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *
|
||||
|
||||
Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob)
|
||||
{
|
||||
ListBase disp = {NULL, NULL};
|
||||
ListBase disp = {nullptr, nullptr};
|
||||
|
||||
if (ob->runtime.curve_cache) {
|
||||
disp = ob->runtime.curve_cache->disp;
|
||||
@@ -578,16 +581,16 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
|
||||
|
||||
Mesh *me_eval = (Mesh *)ob->runtime.data_eval;
|
||||
Mesh *me;
|
||||
MVert *allvert = NULL;
|
||||
MEdge *alledge = NULL;
|
||||
MLoop *allloop = NULL;
|
||||
MLoopUV *alluv = NULL;
|
||||
MPoly *allpoly = NULL;
|
||||
MVert *allvert = nullptr;
|
||||
MEdge *alledge = nullptr;
|
||||
MLoop *allloop = nullptr;
|
||||
MLoopUV *alluv = nullptr;
|
||||
MPoly *allpoly = nullptr;
|
||||
int totvert, totedge, totloop, totpoly;
|
||||
|
||||
Curve *cu = ob->data;
|
||||
Curve *cu = (Curve *)ob->data;
|
||||
|
||||
if (me_eval == NULL) {
|
||||
if (me_eval == nullptr) {
|
||||
if (mesh_nurbs_displist_to_mdata(cu,
|
||||
dispbase,
|
||||
&allvert,
|
||||
@@ -604,30 +607,34 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
|
||||
}
|
||||
|
||||
/* make mesh */
|
||||
me = BKE_id_new_nomain(ID_ME, obdata_name);
|
||||
me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name);
|
||||
|
||||
me->totvert = totvert;
|
||||
me->totedge = totedge;
|
||||
me->totloop = totloop;
|
||||
me->totpoly = totpoly;
|
||||
|
||||
me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert);
|
||||
me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge);
|
||||
me->mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop);
|
||||
me->mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly);
|
||||
me->mvert = (MVert *)CustomData_add_layer(
|
||||
&me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert);
|
||||
me->medge = (MEdge *)CustomData_add_layer(
|
||||
&me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge);
|
||||
me->mloop = (MLoop *)CustomData_add_layer(
|
||||
&me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop);
|
||||
me->mpoly = (MPoly *)CustomData_add_layer(
|
||||
&me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly);
|
||||
|
||||
if (alluv) {
|
||||
const char *uvname = "UVMap";
|
||||
me->mloopuv = CustomData_add_layer_named(
|
||||
me->mloopuv = (MLoopUV *)CustomData_add_layer_named(
|
||||
&me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname);
|
||||
}
|
||||
|
||||
BKE_mesh_calc_normals(me);
|
||||
}
|
||||
else {
|
||||
me = BKE_id_new_nomain(ID_ME, obdata_name);
|
||||
me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name);
|
||||
|
||||
ob->runtime.data_eval = NULL;
|
||||
ob->runtime.data_eval = nullptr;
|
||||
BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true);
|
||||
}
|
||||
|
||||
@@ -636,7 +643,7 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
|
||||
|
||||
mesh_copy_texture_space_from_curve_type(cu, me);
|
||||
|
||||
cu->mat = NULL;
|
||||
cu->mat = nullptr;
|
||||
cu->totcol = 0;
|
||||
|
||||
/* Do not decrement ob->data usercount here,
|
||||
@@ -647,29 +654,29 @@ static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char
|
||||
/* For temporary objects in BKE_mesh_new_from_object don't remap
|
||||
* the entire scene with associated depsgraph updates, which are
|
||||
* problematic for renderers exporting data. */
|
||||
BKE_id_free(NULL, cu);
|
||||
BKE_id_free(nullptr, cu);
|
||||
}
|
||||
|
||||
typedef struct EdgeLink {
|
||||
struct EdgeLink {
|
||||
struct EdgeLink *next, *prev;
|
||||
void *edge;
|
||||
} EdgeLink;
|
||||
};
|
||||
|
||||
typedef struct VertLink {
|
||||
struct VertLink {
|
||||
Link *next, *prev;
|
||||
uint index;
|
||||
} VertLink;
|
||||
};
|
||||
|
||||
static void prependPolyLineVert(ListBase *lb, uint index)
|
||||
{
|
||||
VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink");
|
||||
VertLink *vl = (VertLink *)MEM_callocN(sizeof(VertLink), "VertLink");
|
||||
vl->index = index;
|
||||
BLI_addhead(lb, vl);
|
||||
}
|
||||
|
||||
static void appendPolyLineVert(ListBase *lb, uint index)
|
||||
{
|
||||
VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink");
|
||||
VertLink *vl = (VertLink *)MEM_callocN(sizeof(VertLink), "VertLink");
|
||||
vl->index = index;
|
||||
BLI_addtail(lb, vl);
|
||||
}
|
||||
@@ -689,10 +696,10 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
|
||||
/* only to detect edge polylines */
|
||||
int *edge_users;
|
||||
|
||||
ListBase edges = {NULL, NULL};
|
||||
ListBase edges = {nullptr, nullptr};
|
||||
|
||||
/* get boundary edges */
|
||||
edge_users = MEM_calloc_arrayN(medge_len, sizeof(int), __func__);
|
||||
edge_users = (int *)MEM_calloc_arrayN(medge_len, sizeof(int), __func__);
|
||||
for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) {
|
||||
MLoop *ml = &mloop[mp->loopstart];
|
||||
int j;
|
||||
@@ -705,7 +712,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
|
||||
med = medge;
|
||||
for (i = 0; i < medge_len; i++, med++) {
|
||||
if (edge_users[i] == edge_users_test) {
|
||||
EdgeLink *edl = MEM_callocN(sizeof(EdgeLink), "EdgeLink");
|
||||
EdgeLink *edl = (EdgeLink *)MEM_callocN(sizeof(EdgeLink), "EdgeLink");
|
||||
edl->edge = med;
|
||||
|
||||
BLI_addtail(&edges, edl);
|
||||
@@ -718,10 +725,10 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
|
||||
while (edges.first) {
|
||||
/* each iteration find a polyline and add this as a nurbs poly spline */
|
||||
|
||||
ListBase polyline = {NULL, NULL}; /* store a list of VertLink's */
|
||||
ListBase polyline = {nullptr, nullptr}; /* store a list of VertLink's */
|
||||
bool closed = false;
|
||||
int totpoly = 0;
|
||||
MEdge *med_current = ((EdgeLink *)edges.last)->edge;
|
||||
MEdge *med_current = (MEdge *)((EdgeLink *)edges.last)->edge;
|
||||
uint startVert = med_current->v1;
|
||||
uint endVert = med_current->v2;
|
||||
bool ok = true;
|
||||
@@ -734,12 +741,12 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
|
||||
totedges--;
|
||||
|
||||
while (ok) { /* while connected edges are found... */
|
||||
EdgeLink *edl = edges.last;
|
||||
EdgeLink *edl = (EdgeLink *)edges.last;
|
||||
ok = false;
|
||||
while (edl) {
|
||||
EdgeLink *edl_prev = edl->prev;
|
||||
|
||||
med = edl->edge;
|
||||
med = (MEdge *)edl->edge;
|
||||
|
||||
if (med->v1 == endVert) {
|
||||
endVert = med->v2;
|
||||
@@ -803,7 +810,7 @@ void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int ed
|
||||
nu->bp = (BPoint *)MEM_calloc_arrayN(totpoly, sizeof(BPoint), "bpoints");
|
||||
|
||||
/* add points */
|
||||
vl = polyline.first;
|
||||
vl = (VertLink *)polyline.first;
|
||||
for (i = 0, bp = nu->bp; i < totpoly; i++, bp++, vl = (VertLink *)vl->next) {
|
||||
copy_v3_v3(bp->vec, mvert[vl->index].co);
|
||||
bp->f1 = SELECT;
|
||||
@@ -825,7 +832,7 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene),
|
||||
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
|
||||
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
||||
Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_MESH);
|
||||
ListBase nurblist = {NULL, NULL};
|
||||
ListBase nurblist = {nullptr, nullptr};
|
||||
|
||||
BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 0);
|
||||
BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1);
|
||||
@@ -846,16 +853,13 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(scene),
|
||||
|
||||
void BKE_pointcloud_from_mesh(Mesh *me, PointCloud *pointcloud)
|
||||
{
|
||||
BLI_assert(me != NULL);
|
||||
BLI_assert(me != nullptr);
|
||||
|
||||
pointcloud->totpoint = me->totvert;
|
||||
CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
|
||||
|
||||
/* Copy over all attributes. */
|
||||
const CustomData_MeshMasks mask = {
|
||||
.vmask = CD_MASK_PROP_ALL,
|
||||
};
|
||||
CustomData_merge(&me->vdata, &pointcloud->pdata, mask.vmask, CD_DUPLICATE, me->totvert);
|
||||
CustomData_merge(&me->vdata, &pointcloud->pdata, CD_MASK_PROP_ALL, CD_DUPLICATE, me->totvert);
|
||||
BKE_pointcloud_update_customdata_pointers(pointcloud);
|
||||
CustomData_update_typemap(&pointcloud->pdata);
|
||||
|
||||
@@ -874,7 +878,7 @@ void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce
|
||||
Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
|
||||
Mesh *me_eval = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &CD_MASK_MESH);
|
||||
|
||||
PointCloud *pointcloud = BKE_pointcloud_add(bmain, ob->id.name + 2);
|
||||
PointCloud *pointcloud = (PointCloud *)BKE_pointcloud_add(bmain, ob->id.name + 2);
|
||||
|
||||
BKE_pointcloud_from_mesh(me_eval, pointcloud);
|
||||
|
||||
@@ -889,24 +893,22 @@ void BKE_mesh_to_pointcloud(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce
|
||||
|
||||
void BKE_mesh_from_pointcloud(const PointCloud *pointcloud, Mesh *me)
|
||||
{
|
||||
BLI_assert(pointcloud != NULL);
|
||||
BLI_assert(pointcloud != nullptr);
|
||||
|
||||
me->totvert = pointcloud->totpoint;
|
||||
|
||||
/* Merge over all attributes. */
|
||||
const CustomData_MeshMasks mask = {
|
||||
.vmask = CD_MASK_PROP_ALL,
|
||||
};
|
||||
CustomData_merge(&pointcloud->pdata, &me->vdata, mask.vmask, CD_DUPLICATE, pointcloud->totpoint);
|
||||
CustomData_merge(
|
||||
&pointcloud->pdata, &me->vdata, CD_MASK_PROP_ALL, CD_DUPLICATE, pointcloud->totpoint);
|
||||
|
||||
/* Convert the Position attribute to a mesh vertex. */
|
||||
me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert);
|
||||
me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, nullptr, me->totvert);
|
||||
CustomData_update_typemap(&me->vdata);
|
||||
|
||||
const int layer_idx = CustomData_get_named_layer_index(
|
||||
&me->vdata, CD_PROP_FLOAT3, POINTCLOUD_ATTR_POSITION);
|
||||
CustomDataLayer *pos_layer = &me->vdata.layers[layer_idx];
|
||||
float(*positions)[3] = pos_layer->data;
|
||||
float(*positions)[3] = (float(*)[3])pos_layer->data;
|
||||
|
||||
MVert *mvert;
|
||||
mvert = me->mvert;
|
||||
@@ -956,7 +958,8 @@ static Object *object_for_curve_to_mesh_create(Object *object)
|
||||
Curve *curve = (Curve *)object->data;
|
||||
|
||||
/* Create object itself. */
|
||||
Object *temp_object = (Object *)BKE_id_copy_ex(NULL, &object->id, NULL, LIB_ID_COPY_LOCALIZE);
|
||||
Object *temp_object = (Object *)BKE_id_copy_ex(
|
||||
nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE);
|
||||
|
||||
/* Remove all modifiers, since we don't want them to be applied. */
|
||||
BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT);
|
||||
@@ -965,26 +968,27 @@ static Object *object_for_curve_to_mesh_create(Object *object)
|
||||
*
|
||||
* Note that there are extra fields in there like bevel and path, but those are not needed during
|
||||
* conversion, so they are not copied to save unnecessary allocations. */
|
||||
if (temp_object->runtime.curve_cache == NULL) {
|
||||
temp_object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache),
|
||||
"CurveCache for curve types");
|
||||
if (temp_object->runtime.curve_cache == nullptr) {
|
||||
temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
|
||||
"CurveCache for curve types");
|
||||
}
|
||||
|
||||
if (object->runtime.curve_cache != NULL) {
|
||||
if (object->runtime.curve_cache != nullptr) {
|
||||
BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp);
|
||||
}
|
||||
|
||||
/* Constructive modifiers will use mesh to store result. */
|
||||
if (object->runtime.data_eval != NULL) {
|
||||
if (object->runtime.data_eval != nullptr) {
|
||||
BKE_id_copy_ex(
|
||||
NULL, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE);
|
||||
nullptr, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE);
|
||||
}
|
||||
|
||||
/* Need to create copy of curve itself as well, it will be freed by underlying conversion
|
||||
* functions.
|
||||
*
|
||||
* NOTE: Copies the data, but not the shapekeys. */
|
||||
BKE_id_copy_ex(NULL, object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE);
|
||||
BKE_id_copy_ex(
|
||||
nullptr, (const ID *)object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE);
|
||||
Curve *temp_curve = (Curve *)temp_object->data;
|
||||
|
||||
/* Make sure texture space is calculated for a copy of curve, it will be used for the final
|
||||
@@ -1011,8 +1015,9 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
|
||||
remapped_object.data = &remapped_curve;
|
||||
|
||||
if (object->runtime.curve_cache == NULL) {
|
||||
object->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for Curve");
|
||||
if (object->runtime.curve_cache == nullptr) {
|
||||
object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache),
|
||||
"CurveCache for Curve");
|
||||
}
|
||||
|
||||
/* Temporarily share the curve-cache with the temporary object, owned by `object`. */
|
||||
@@ -1025,8 +1030,8 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
*
|
||||
* So we create temporary copy of the object which will use same data as the original bevel, but
|
||||
* will have no modifiers. */
|
||||
Object bevel_object = {{NULL}};
|
||||
if (remapped_curve.bevobj != NULL) {
|
||||
Object bevel_object = {{nullptr}};
|
||||
if (remapped_curve.bevobj != nullptr) {
|
||||
bevel_object = *remapped_curve.bevobj;
|
||||
BLI_listbase_clear(&bevel_object.modifiers);
|
||||
BKE_object_runtime_reset(&bevel_object);
|
||||
@@ -1034,34 +1039,34 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
}
|
||||
|
||||
/* Same thing for taper. */
|
||||
Object taper_object = {{NULL}};
|
||||
if (remapped_curve.taperobj != NULL) {
|
||||
Object taper_object = {{nullptr}};
|
||||
if (remapped_curve.taperobj != nullptr) {
|
||||
taper_object = *remapped_curve.taperobj;
|
||||
BLI_listbase_clear(&taper_object.modifiers);
|
||||
BKE_object_runtime_reset(&taper_object);
|
||||
remapped_curve.taperobj = &taper_object;
|
||||
}
|
||||
|
||||
/* NOTE: We don't have dependency graph or scene here, so we pass NULL. This is all fine since
|
||||
/* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since
|
||||
* they are only used for modifier stack, which we have explicitly disabled for all objects.
|
||||
*
|
||||
* TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a
|
||||
* bit of internal functions (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also
|
||||
* Mesh From Curve operator.
|
||||
* Brecht says hold off with that. */
|
||||
Mesh *mesh_eval = NULL;
|
||||
Mesh *mesh_eval = nullptr;
|
||||
BKE_displist_make_curveTypes_forRender(
|
||||
NULL, NULL, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval);
|
||||
nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval);
|
||||
|
||||
/* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a
|
||||
* real issue currently, code here is broken in more than one way, fix(es) will be done
|
||||
* separately. */
|
||||
if (mesh_eval != NULL) {
|
||||
if (mesh_eval != nullptr) {
|
||||
BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true);
|
||||
}
|
||||
|
||||
/* Owned by `object` & needed by the caller to create the mesh. */
|
||||
remapped_object.runtime.curve_cache = NULL;
|
||||
remapped_object.runtime.curve_cache = nullptr;
|
||||
|
||||
BKE_object_runtime_free_data(&remapped_object);
|
||||
BKE_object_runtime_free_data(&taper_object);
|
||||
@@ -1070,7 +1075,7 @@ static void curve_to_mesh_eval_ensure(Object *object)
|
||||
|
||||
static Mesh *mesh_new_from_curve_type_object(Object *object)
|
||||
{
|
||||
Curve *curve = object->data;
|
||||
Curve *curve = (Curve *)object->data;
|
||||
Object *temp_object = object_for_curve_to_mesh_create(object);
|
||||
Curve *temp_curve = (Curve *)temp_object->data;
|
||||
|
||||
@@ -1081,8 +1086,8 @@ static Mesh *mesh_new_from_curve_type_object(Object *object)
|
||||
}
|
||||
|
||||
/* Reset pointers before conversion. */
|
||||
temp_curve->editfont = NULL;
|
||||
temp_curve->editnurb = NULL;
|
||||
temp_curve->editfont = nullptr;
|
||||
temp_curve->editnurb = nullptr;
|
||||
|
||||
/* Convert to mesh. */
|
||||
mesh_from_nurbs_displist(
|
||||
@@ -1091,14 +1096,14 @@ static Mesh *mesh_new_from_curve_type_object(Object *object)
|
||||
/* #mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't
|
||||
* the curve did not have any segments or otherwise would have generated an empty mesh. */
|
||||
if (temp_object->type != OB_MESH) {
|
||||
BKE_id_free(NULL, temp_object->data);
|
||||
BKE_id_free(NULL, temp_object);
|
||||
return NULL;
|
||||
BKE_id_free(nullptr, temp_object->data);
|
||||
BKE_id_free(nullptr, temp_object);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mesh *mesh_result = temp_object->data;
|
||||
Mesh *mesh_result = (Mesh *)temp_object->data;
|
||||
|
||||
BKE_id_free(NULL, temp_object);
|
||||
BKE_id_free(nullptr, temp_object);
|
||||
|
||||
/* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */
|
||||
|
||||
@@ -1114,19 +1119,19 @@ static Mesh *mesh_new_from_mball_object(Object *object)
|
||||
* ball).
|
||||
*
|
||||
* We create empty mesh so scripters don't run into None objects. */
|
||||
if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == NULL ||
|
||||
if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == nullptr ||
|
||||
BLI_listbase_is_empty(&object->runtime.curve_cache->disp)) {
|
||||
return BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
|
||||
return (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
|
||||
}
|
||||
|
||||
Mesh *mesh_result = BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
|
||||
Mesh *mesh_result = (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2);
|
||||
BKE_mesh_from_metaball(&object->runtime.curve_cache->disp, mesh_result);
|
||||
BKE_mesh_texspace_copy_from_object(mesh_result, object);
|
||||
|
||||
/* Copy materials. */
|
||||
mesh_result->totcol = mball->totcol;
|
||||
mesh_result->mat = MEM_dupallocN(mball->mat);
|
||||
if (mball->mat != NULL) {
|
||||
mesh_result->mat = (Material **)MEM_dupallocN(mball->mat);
|
||||
if (mball->mat != nullptr) {
|
||||
for (int i = mball->totcol; i-- > 0;) {
|
||||
mesh_result->mat[i] = BKE_object_material_get(object, i + 1);
|
||||
}
|
||||
@@ -1142,7 +1147,7 @@ static Mesh *mesh_new_from_mesh(Object *object, Mesh *mesh)
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh);
|
||||
|
||||
Mesh *mesh_result = (Mesh *)BKE_id_copy_ex(
|
||||
NULL, &mesh->id, NULL, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT);
|
||||
nullptr, &mesh->id, nullptr, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT);
|
||||
/* NOTE: Materials should already be copied. */
|
||||
/* Copy original mesh name. This is because edit meshes might not have one properly set name. */
|
||||
BLI_strncpy(mesh_result->id.name, ((ID *)object->data)->name, sizeof(mesh_result->id.name));
|
||||
@@ -1157,12 +1162,12 @@ static Mesh *mesh_new_from_mesh_object_with_layers(Depsgraph *depsgraph,
|
||||
return mesh_new_from_mesh(object, (Mesh *)object->data);
|
||||
}
|
||||
|
||||
if (depsgraph == NULL) {
|
||||
return NULL;
|
||||
if (depsgraph == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Object object_for_eval = *object;
|
||||
if (object_for_eval.runtime.data_orig != NULL) {
|
||||
if (object_for_eval.runtime.data_orig != nullptr) {
|
||||
object_for_eval.data = object_for_eval.runtime.data_orig;
|
||||
}
|
||||
|
||||
@@ -1186,10 +1191,10 @@ static Mesh *mesh_new_from_mesh_object(Depsgraph *depsgraph,
|
||||
if (preserve_all_data_layers || preserve_origindex) {
|
||||
return mesh_new_from_mesh_object_with_layers(depsgraph, object, preserve_origindex);
|
||||
}
|
||||
Mesh *mesh_input = object->data;
|
||||
Mesh *mesh_input = (Mesh *)object->data;
|
||||
/* If we are in edit mode, use evaluated mesh from edit structure, matching to what
|
||||
* viewport is using for visualization. */
|
||||
if (mesh_input->edit_mesh != NULL && mesh_input->edit_mesh->mesh_eval_final) {
|
||||
if (mesh_input->edit_mesh != nullptr && mesh_input->edit_mesh->mesh_eval_final) {
|
||||
mesh_input = mesh_input->edit_mesh->mesh_eval_final;
|
||||
}
|
||||
return mesh_new_from_mesh(object, mesh_input);
|
||||
@@ -1200,7 +1205,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
|
||||
const bool preserve_all_data_layers,
|
||||
const bool preserve_origindex)
|
||||
{
|
||||
Mesh *new_mesh = NULL;
|
||||
Mesh *new_mesh = nullptr;
|
||||
switch (object->type) {
|
||||
case OB_FONT:
|
||||
case OB_CURVE:
|
||||
@@ -1216,11 +1221,11 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
|
||||
break;
|
||||
default:
|
||||
/* Object does not have geometry data. */
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
if (new_mesh == NULL) {
|
||||
if (new_mesh == nullptr) {
|
||||
/* Happens in special cases like request of mesh for non-mother meta ball. */
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* The result must have 0 users, since it's just a mesh which is free-dangling data-block.
|
||||
@@ -1233,9 +1238,9 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
|
||||
* ownership.
|
||||
*
|
||||
* Here we are constructing a mesh which is supposed to be independent, which means no shared
|
||||
* ownership is allowed, so we make sure edit mesh is reset to NULL (which is similar to as if
|
||||
* ownership is allowed, so we make sure edit mesh is reset to nullptr (which is similar to as if
|
||||
* one duplicates the objects and applies all the modifiers). */
|
||||
new_mesh->edit_mesh = NULL;
|
||||
new_mesh->edit_mesh = nullptr;
|
||||
|
||||
return new_mesh;
|
||||
}
|
||||
@@ -1243,7 +1248,7 @@ Mesh *BKE_mesh_new_from_object(Depsgraph *depsgraph,
|
||||
static int foreach_libblock_make_original_callback(LibraryIDLinkCallbackData *cb_data)
|
||||
{
|
||||
ID **id_p = cb_data->id_pointer;
|
||||
if (*id_p == NULL) {
|
||||
if (*id_p == nullptr) {
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
*id_p = DEG_get_original_id(*id_p);
|
||||
@@ -1254,7 +1259,7 @@ static int foreach_libblock_make_original_callback(LibraryIDLinkCallbackData *cb
|
||||
static int foreach_libblock_make_usercounts_callback(LibraryIDLinkCallbackData *cb_data)
|
||||
{
|
||||
ID **id_p = cb_data->id_pointer;
|
||||
if (*id_p == NULL) {
|
||||
if (*id_p == nullptr) {
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
@@ -1278,7 +1283,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
|
||||
BLI_assert(ELEM(object->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_MESH));
|
||||
|
||||
Mesh *mesh = BKE_mesh_new_from_object(depsgraph, object, preserve_all_data_layers, false);
|
||||
if (mesh == NULL) {
|
||||
if (mesh == nullptr) {
|
||||
/* Unable to convert the object to a mesh, return an empty one. */
|
||||
Mesh *mesh_in_bmain = BKE_mesh_add(bmain, ((ID *)object->data)->name + 2);
|
||||
id_us_min(&mesh_in_bmain->id);
|
||||
@@ -1294,7 +1299,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
|
||||
* Note that user-count updates has to be done *after* mesh has been transferred to Main database
|
||||
* (since doing refcounting on non-Main IDs is forbidden). */
|
||||
BKE_library_foreach_ID_link(
|
||||
NULL, &mesh->id, foreach_libblock_make_original_callback, NULL, IDWALK_NOP);
|
||||
nullptr, &mesh->id, foreach_libblock_make_original_callback, nullptr, IDWALK_NOP);
|
||||
|
||||
/* Append the mesh to 'bmain'.
|
||||
* We do it a bit longer way since there is no simple and clear way of adding existing data-block
|
||||
@@ -1311,14 +1316,14 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain,
|
||||
mesh_in_bmain->totcol = mesh->totcol;
|
||||
mesh_in_bmain->flag = mesh->flag;
|
||||
mesh_in_bmain->smoothresh = mesh->smoothresh;
|
||||
mesh->mat = NULL;
|
||||
mesh->mat = nullptr;
|
||||
|
||||
BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, NULL, &CD_MASK_MESH, true);
|
||||
BKE_mesh_nomain_to_mesh(mesh, mesh_in_bmain, nullptr, &CD_MASK_MESH, true);
|
||||
|
||||
/* User-count is required because so far mesh was in a limbo, where library management does
|
||||
* not perform any user management (i.e. copy of a mesh will not increase users of materials). */
|
||||
BKE_library_foreach_ID_link(
|
||||
NULL, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, NULL, IDWALK_NOP);
|
||||
nullptr, &mesh_in_bmain->id, foreach_libblock_make_usercounts_callback, nullptr, IDWALK_NOP);
|
||||
|
||||
/* Make sure user count from BKE_mesh_add() is the one we expect here and bring it down to 0. */
|
||||
BLI_assert(mesh_in_bmain->id.us == 1);
|
||||
@@ -1347,7 +1352,7 @@ static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src)
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0, kb = key->block.first; kb; kb = kb->next, i++) {
|
||||
for (i = 0, kb = (KeyBlock *)key->block.first; kb; kb = kb->next, i++) {
|
||||
int ci;
|
||||
float *array;
|
||||
|
||||
@@ -1358,10 +1363,10 @@ static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src)
|
||||
mesh_src->totvert,
|
||||
kb->name,
|
||||
kb->totelem);
|
||||
array = MEM_calloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
|
||||
array = (float *)MEM_calloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
|
||||
}
|
||||
else {
|
||||
array = MEM_malloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
|
||||
array = (float *)MEM_malloc_arrayN((size_t)mesh_src->totvert, sizeof(float[3]), __func__);
|
||||
memcpy(array, kb->data, sizeof(float[3]) * (size_t)mesh_src->totvert);
|
||||
}
|
||||
|
||||
@@ -1379,9 +1384,10 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
|
||||
ModifierData *md_eval,
|
||||
const bool build_shapekey_layers)
|
||||
{
|
||||
Mesh *me = ob_eval->runtime.data_orig ? ob_eval->runtime.data_orig : ob_eval->data;
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info(md_eval->type);
|
||||
Mesh *result = NULL;
|
||||
Mesh *me = ob_eval->runtime.data_orig ? (Mesh *)ob_eval->runtime.data_orig :
|
||||
(Mesh *)ob_eval->data;
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md_eval->type);
|
||||
Mesh *result = nullptr;
|
||||
KeyBlock *kb;
|
||||
ModifierEvalContext mectx = {depsgraph, ob_eval, MOD_APPLY_TO_BASE_MESH};
|
||||
|
||||
@@ -1389,12 +1395,12 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
|
||||
return result;
|
||||
}
|
||||
|
||||
if (mti->isDisabled && mti->isDisabled(scene, md_eval, 0)) {
|
||||
if (mti->isDisabled && mti->isDisabled(scene, md_eval, false)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (build_shapekey_layers && me->key &&
|
||||
(kb = BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) {
|
||||
(kb = (KeyBlock *)BLI_findlink(&me->key->block, ob_eval->shapenr - 1))) {
|
||||
BKE_keyblock_convert_to_mesh(kb, me);
|
||||
}
|
||||
|
||||
@@ -1402,7 +1408,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
|
||||
int numVerts;
|
||||
float(*deformedVerts)[3] = BKE_mesh_vert_coords_alloc(me, &numVerts);
|
||||
|
||||
result = (Mesh *)BKE_id_copy_ex(NULL, &me->id, NULL, LIB_ID_COPY_LOCALIZE);
|
||||
result = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE);
|
||||
mti->deformVerts(md_eval, &mectx, result, deformedVerts, numVerts);
|
||||
BKE_mesh_vert_coords_apply(result, deformedVerts);
|
||||
|
||||
@@ -1413,7 +1419,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
|
||||
MEM_freeN(deformedVerts);
|
||||
}
|
||||
else {
|
||||
Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(NULL, &me->id, NULL, LIB_ID_COPY_LOCALIZE);
|
||||
Mesh *mesh_temp = (Mesh *)BKE_id_copy_ex(nullptr, &me->id, nullptr, LIB_ID_COPY_LOCALIZE);
|
||||
|
||||
if (build_shapekey_layers) {
|
||||
add_shapekey_layers(mesh_temp, me);
|
||||
@@ -1423,7 +1429,7 @@ Mesh *BKE_mesh_create_derived_for_modifier(struct Depsgraph *depsgraph,
|
||||
ASSERT_IS_VALID_MESH(result);
|
||||
|
||||
if (mesh_temp != result) {
|
||||
BKE_id_free(NULL, mesh_temp);
|
||||
BKE_id_free(nullptr, mesh_temp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1446,7 +1452,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act
|
||||
&mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)];
|
||||
float(*cos)[3], (*kbcos)[3];
|
||||
|
||||
for (kb = mesh_dst->key->block.first; kb; kb = kb->next) {
|
||||
for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) {
|
||||
if (kb->uid == layer->uid) {
|
||||
break;
|
||||
}
|
||||
@@ -1461,10 +1467,10 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act
|
||||
MEM_freeN(kb->data);
|
||||
}
|
||||
|
||||
cos = CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i);
|
||||
cos = (float(*)[3])CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i);
|
||||
kb->totelem = mesh_src->totvert;
|
||||
|
||||
kb->data = kbcos = MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__);
|
||||
kb->data = kbcos = (float(*)[3])MEM_malloc_arrayN(kb->totelem, sizeof(float[3]), __func__);
|
||||
if (kb->uid == actshape_uid) {
|
||||
MVert *mvert = mesh_src->mvert;
|
||||
|
||||
@@ -1479,7 +1485,7 @@ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int act
|
||||
}
|
||||
}
|
||||
|
||||
for (kb = mesh_dst->key->block.first; kb; kb = kb->next) {
|
||||
for (kb = (KeyBlock *)mesh_dst->key->block.first; kb; kb = kb->next) {
|
||||
if (kb->totelem != mesh_src->totvert) {
|
||||
if (kb->data) {
|
||||
MEM_freeN(kb->data);
|
||||
@@ -1544,7 +1550,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
int uid;
|
||||
|
||||
if (ob) {
|
||||
kb = BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1);
|
||||
kb = (KeyBlock *)BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1);
|
||||
if (kb) {
|
||||
uid = kb->uid;
|
||||
}
|
||||
@@ -1607,11 +1613,11 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
/* NOTE(nazgul): maybe some other layers should be copied? */
|
||||
if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) {
|
||||
if (totloop == mesh_dst->totloop) {
|
||||
MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
|
||||
MDisps *mdisps = (MDisps *)CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS);
|
||||
CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop);
|
||||
if (alloctype == CD_ASSIGN) {
|
||||
/* Assign NULL to prevent double-free. */
|
||||
CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, NULL);
|
||||
/* Assign nullptr to prevent double-free. */
|
||||
CustomData_set_layer(&mesh_dst->ldata, CD_MDISPS, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1633,7 +1639,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) {
|
||||
id_us_min(&tmp.key->id);
|
||||
}
|
||||
tmp.key = NULL;
|
||||
tmp.key = nullptr;
|
||||
}
|
||||
|
||||
/* Clear selection history */
|
||||
@@ -1660,7 +1666,7 @@ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src,
|
||||
CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask->lmask);
|
||||
CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask->pmask);
|
||||
}
|
||||
BKE_id_free(NULL, mesh_src);
|
||||
BKE_id_free(nullptr, mesh_src);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1682,7 +1688,7 @@ void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb)
|
||||
kb->data = MEM_malloc_arrayN(mesh_dst->key->elemsize, mesh_dst->totvert, "kb->data");
|
||||
kb->totelem = totvert;
|
||||
|
||||
fp = kb->data;
|
||||
fp = (float *)kb->data;
|
||||
mvert = mesh_src->mvert;
|
||||
|
||||
for (a = 0; a < kb->totelem; a++, fp += 3, mvert++) {
|
@@ -653,6 +653,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
|
||||
BLO_read_list(reader, &ntree->nodes);
|
||||
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
|
||||
node->typeinfo = nullptr;
|
||||
node->declaration = nullptr;
|
||||
|
||||
BLO_read_list(reader, &node->inputs);
|
||||
BLO_read_list(reader, &node->outputs);
|
||||
@@ -1014,10 +1015,8 @@ IDTypeInfo IDType_ID_NT = {
|
||||
static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype)
|
||||
{
|
||||
if (ntype->declare != nullptr) {
|
||||
blender::nodes::NodeDeclaration node_decl;
|
||||
blender::nodes::NodeDeclarationBuilder builder{node_decl};
|
||||
ntype->declare(builder);
|
||||
node_decl.build(*ntree, *node);
|
||||
nodeDeclarationEnsure(ntree, node);
|
||||
node->declaration->build(*ntree, *node);
|
||||
return;
|
||||
}
|
||||
bNodeSocketTemplate *sockdef;
|
||||
@@ -2216,6 +2215,10 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree,
|
||||
bNodeLink *link_dst, *link_src;
|
||||
|
||||
*node_dst = *node_src;
|
||||
|
||||
/* Reset the declaration of the new node. */
|
||||
node_dst->declaration = nullptr;
|
||||
|
||||
/* can be called for nodes outside a node tree (e.g. clipboard) */
|
||||
if (ntree) {
|
||||
if (unique_name) {
|
||||
@@ -3103,6 +3106,8 @@ static void node_free_node(bNodeTree *ntree, bNode *node)
|
||||
MEM_freeN(node->prop);
|
||||
}
|
||||
|
||||
delete node->declaration;
|
||||
|
||||
MEM_freeN(node);
|
||||
|
||||
if (ntree) {
|
||||
@@ -3933,6 +3938,24 @@ int nodeSocketLinkLimit(const bNodeSocket *sock)
|
||||
return sock->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the node implements a `declare` function, this function makes sure that `node->declaration`
|
||||
* is up to date.
|
||||
*/
|
||||
void nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node)
|
||||
{
|
||||
if (node->typeinfo->declare == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (node->declaration != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
node->declaration = new blender::nodes::NodeDeclaration();
|
||||
blender::nodes::NodeDeclarationBuilder builder{*node->declaration};
|
||||
node->typeinfo->declare(builder);
|
||||
}
|
||||
|
||||
/* ************** Node Clipboard *********** */
|
||||
|
||||
#define USE_NODE_CB_VALIDATE
|
||||
@@ -5133,6 +5156,9 @@ static void registerGeometryNodes()
|
||||
{
|
||||
register_node_type_geo_group();
|
||||
|
||||
register_node_type_geo_legacy_material_assign();
|
||||
register_node_type_geo_legacy_select_by_material();
|
||||
|
||||
register_node_type_geo_align_rotation_to_vector();
|
||||
register_node_type_geo_attribute_clamp();
|
||||
register_node_type_geo_attribute_color_ramp();
|
||||
@@ -5171,6 +5197,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_curve_set_handles();
|
||||
register_node_type_geo_curve_spline_type();
|
||||
register_node_type_geo_curve_subdivide();
|
||||
register_node_type_geo_curve_fillet();
|
||||
register_node_type_geo_curve_to_mesh();
|
||||
register_node_type_geo_curve_to_points();
|
||||
register_node_type_geo_curve_trim();
|
||||
@@ -5205,7 +5232,7 @@ static void registerGeometryNodes()
|
||||
register_node_type_geo_raycast();
|
||||
register_node_type_geo_sample_texture();
|
||||
register_node_type_geo_select_by_handle_type();
|
||||
register_node_type_geo_select_by_material();
|
||||
register_node_type_geo_material_selection();
|
||||
register_node_type_geo_separate_components();
|
||||
register_node_type_geo_set_position();
|
||||
register_node_type_geo_subdivision_surface();
|
||||
|
@@ -312,7 +312,7 @@ IDTypeInfo IDType_ID_SCR = {
|
||||
.name = "Screen",
|
||||
.name_plural = "screens",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_SCREEN,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
|
||||
.init_data = NULL,
|
||||
.copy_data = NULL,
|
||||
|
@@ -1213,7 +1213,6 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so
|
||||
AUD_SoundInfo info = AUD_getInfo(playback_handle);
|
||||
sound_info->specs.channels = (eSoundChannels)info.specs.channels;
|
||||
sound_info->length = info.length;
|
||||
sound_info->start_offset = info.start_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1231,6 +1230,44 @@ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *soun
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BKE_sound_stream_info_get(struct Main *main, const char *filepath, int stream, SoundStreamInfo *sound_info)
|
||||
{
|
||||
const char *path;
|
||||
char str[FILE_MAX];
|
||||
AUD_Sound *sound;
|
||||
AUD_StreamInfo *stream_infos;
|
||||
int stream_count;
|
||||
|
||||
BLI_strncpy(str, filepath, sizeof(str));
|
||||
path = BKE_main_blendfile_path(main);
|
||||
BLI_path_abs(str, path);
|
||||
|
||||
sound = AUD_Sound_file(str);
|
||||
if (!sound) {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream_count = AUD_Sound_getFileStreams(sound, &stream_infos);
|
||||
|
||||
AUD_Sound_free(sound);
|
||||
|
||||
if (!stream_infos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((stream < 0) || (stream >= stream_count)) {
|
||||
free(stream_infos);
|
||||
return false;
|
||||
}
|
||||
|
||||
sound_info->start = stream_infos[stream].start;
|
||||
sound_info->duration = stream_infos[stream].duration;
|
||||
|
||||
free(stream_infos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else /* WITH_AUDASPACE */
|
||||
|
||||
# include "BLI_utildefines.h"
|
||||
@@ -1400,6 +1437,14 @@ bool BKE_sound_info_get(struct Main *UNUSED(main),
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BKE_sound_stream_info_get(struct Main *UNUSED(main),
|
||||
const char *UNUSED(filepath),
|
||||
int UNUSED(stream),
|
||||
SoundStreamInfo *UNUSED(sound_info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* WITH_AUDASPACE */
|
||||
|
||||
void BKE_sound_reset_scene_runtime(Scene *scene)
|
||||
|
@@ -19,6 +19,8 @@
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "BKE_attribute_access.hh"
|
||||
#include "BKE_attribute_math.hh"
|
||||
#include "BKE_spline.hh"
|
||||
|
||||
#include "FN_generic_virtual_array.hh"
|
||||
@@ -28,6 +30,8 @@ using blender::float3;
|
||||
using blender::IndexRange;
|
||||
using blender::MutableSpan;
|
||||
using blender::Span;
|
||||
using blender::attribute_math::convert_to_static_type;
|
||||
using blender::bke::AttributeIDRef;
|
||||
using blender::fn::GMutableSpan;
|
||||
using blender::fn::GSpan;
|
||||
using blender::fn::GVArray;
|
||||
@@ -110,10 +114,36 @@ void Spline::transform(const blender::float4x4 &matrix)
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
void Spline::reverse()
|
||||
{
|
||||
this->positions().reverse();
|
||||
this->radii().reverse();
|
||||
this->tilts().reverse();
|
||||
|
||||
this->attributes.foreach_attribute(
|
||||
[&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
|
||||
std::optional<blender::fn::GMutableSpan> attribute = this->attributes.get_for_write(id);
|
||||
if (!attribute) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
convert_to_static_type(meta_data.data_type, [&](auto dummy) {
|
||||
using T = decltype(dummy);
|
||||
attribute->typed<T>().reverse();
|
||||
});
|
||||
return true;
|
||||
},
|
||||
ATTR_DOMAIN_POINT);
|
||||
|
||||
this->reverse_impl();
|
||||
this->mark_cache_invalid();
|
||||
}
|
||||
|
||||
int Spline::evaluated_edges_size() const
|
||||
{
|
||||
const int eval_size = this->evaluated_points_size();
|
||||
if (eval_size == 1) {
|
||||
if (eval_size < 2) {
|
||||
/* Two points are required for an edge. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -161,7 +191,7 @@ static void accumulate_lengths(Span<float3> positions,
|
||||
* Return non-owning access to the cache of accumulated lengths along the spline. Each item is the
|
||||
* length of the subsequent segment, i.e. the first value is the length of the first segment rather
|
||||
* than 0. This calculation is rather trivial, and only depends on the evaluated positions.
|
||||
* However, the results are used often, so it makes sense to cache it.
|
||||
* However, the results are used often, and it is necessarily single threaded, so it is cached.
|
||||
*/
|
||||
Span<float> Spline::evaluated_lengths() const
|
||||
{
|
||||
@@ -176,9 +206,10 @@ Span<float> Spline::evaluated_lengths() const
|
||||
|
||||
const int total = evaluated_edges_size();
|
||||
evaluated_lengths_cache_.resize(total);
|
||||
|
||||
Span<float3> positions = this->evaluated_positions();
|
||||
accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
|
||||
if (total != 0) {
|
||||
Span<float3> positions = this->evaluated_positions();
|
||||
accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_);
|
||||
}
|
||||
|
||||
length_cache_dirty_ = false;
|
||||
return evaluated_lengths_cache_;
|
||||
|
@@ -166,6 +166,17 @@ MutableSpan<float3> BezierSpline::handle_positions_right()
|
||||
return handle_positions_right_;
|
||||
}
|
||||
|
||||
void BezierSpline::reverse_impl()
|
||||
{
|
||||
this->handle_positions_left().reverse();
|
||||
this->handle_positions_right().reverse();
|
||||
std::swap(this->handle_positions_left_, this->handle_positions_right_);
|
||||
|
||||
this->handle_types_left().reverse();
|
||||
this->handle_types_right().reverse();
|
||||
std::swap(this->handle_types_left_, this->handle_types_right_);
|
||||
}
|
||||
|
||||
static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
|
||||
{
|
||||
if (i == 0) {
|
||||
|
@@ -142,6 +142,11 @@ Span<float> NURBSpline::weights() const
|
||||
return weights_;
|
||||
}
|
||||
|
||||
void NURBSpline::reverse_impl()
|
||||
{
|
||||
this->weights().reverse();
|
||||
}
|
||||
|
||||
void NURBSpline::mark_cache_invalid()
|
||||
{
|
||||
basis_cache_dirty_ = true;
|
||||
|
@@ -91,6 +91,10 @@ Span<float> PolySpline::tilts() const
|
||||
return tilts_;
|
||||
}
|
||||
|
||||
void PolySpline::reverse_impl()
|
||||
{
|
||||
}
|
||||
|
||||
void PolySpline::mark_cache_invalid()
|
||||
{
|
||||
tangent_cache_dirty_ = true;
|
||||
|
@@ -1232,7 +1232,7 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
|
||||
// BKE_mesh_validate(result, true, true);
|
||||
BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
|
||||
if (!subdiv_context.can_evaluate_normals) {
|
||||
result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
|
||||
BKE_mesh_normals_tag_dirty(result);
|
||||
}
|
||||
/* Free used memory. */
|
||||
subdiv_mesh_context_free(&subdiv_context);
|
||||
|
@@ -744,16 +744,15 @@ static UndoStep *undosys_step_iter_first(UndoStep *us_reference, const eUndoStep
|
||||
/**
|
||||
* Undo/Redo until the given `us_target` step becomes the active (currently loaded) one.
|
||||
*
|
||||
* \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the
|
||||
* active step.
|
||||
* \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target`
|
||||
* will become the active step.
|
||||
*
|
||||
* \note In case `use_skip` is true, the final target will always be **beyond** the given one (if
|
||||
* the given one has to be skipped).
|
||||
* \note In case `use_skip` is true, the final target will always be **beyond** the given one
|
||||
* (if the given one has to be skipped).
|
||||
*
|
||||
* \param us_reference If NULL, will be set to current active step in the undo stack. Otherwise, it
|
||||
* is assumed to match the current state, and will be used as basis for the
|
||||
* undo/redo process (i.e. all steps in-between `us_reference` and `us_target`
|
||||
* will be processed).
|
||||
* \param us_reference: If NULL, will be set to current active step in the undo stack. Otherwise,
|
||||
* it is assumed to match the current state, and will be used as basis for the undo/redo process
|
||||
* (i.e. all steps in-between `us_reference` and `us_target` will be processed).
|
||||
*/
|
||||
bool BKE_undosys_step_load_data_ex(UndoStack *ustack,
|
||||
bContext *C,
|
||||
|
@@ -186,7 +186,7 @@ IDTypeInfo IDType_ID_WS = {
|
||||
.name = "WorkSpace",
|
||||
.name_plural = "workspaces",
|
||||
.translation_context = BLT_I18NCONTEXT_ID_WORKSPACE,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_MAKELOCAL | IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
.flags = IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA,
|
||||
|
||||
.init_data = workspace_init_data,
|
||||
.copy_data = NULL,
|
||||
|
@@ -39,6 +39,7 @@
|
||||
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
@@ -221,6 +222,8 @@ class IndexMask {
|
||||
{
|
||||
return indices_.is_empty();
|
||||
}
|
||||
|
||||
IndexMask slice_and_offset(IndexRange slice, Vector<int64_t> &r_new_indices) const;
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
|
@@ -50,11 +50,10 @@ class ResourceScope : NonCopyable, NonMovable {
|
||||
struct ResourceData {
|
||||
void *data;
|
||||
void (*free)(void *data);
|
||||
const char *debug_name;
|
||||
};
|
||||
|
||||
LinearAllocator<> m_allocator;
|
||||
Vector<ResourceData> m_resources;
|
||||
LinearAllocator<> allocator_;
|
||||
Vector<ResourceData> resources_;
|
||||
|
||||
public:
|
||||
ResourceScope() = default;
|
||||
@@ -62,8 +61,8 @@ class ResourceScope : NonCopyable, NonMovable {
|
||||
~ResourceScope()
|
||||
{
|
||||
/* Free in reversed order. */
|
||||
for (int64_t i = m_resources.size(); i--;) {
|
||||
ResourceData &data = m_resources[i];
|
||||
for (int64_t i = resources_.size(); i--;) {
|
||||
ResourceData &data = resources_[i];
|
||||
data.free(data.data);
|
||||
}
|
||||
}
|
||||
@@ -72,20 +71,17 @@ class ResourceScope : NonCopyable, NonMovable {
|
||||
* Pass ownership of the resource to the ResourceScope. It will be destructed and freed when
|
||||
* the collector is destructed.
|
||||
*/
|
||||
template<typename T> T *add(std::unique_ptr<T> resource, const char *name)
|
||||
template<typename T> T *add(std::unique_ptr<T> resource)
|
||||
{
|
||||
BLI_assert(resource.get() != nullptr);
|
||||
T *ptr = resource.release();
|
||||
if (ptr == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
this->add(
|
||||
ptr,
|
||||
[](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
delete typed_data;
|
||||
},
|
||||
name);
|
||||
this->add(ptr, [](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
delete typed_data;
|
||||
});
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -93,7 +89,7 @@ class ResourceScope : NonCopyable, NonMovable {
|
||||
* Pass ownership of the resource to the ResourceScope. It will be destructed when the
|
||||
* collector is destructed.
|
||||
*/
|
||||
template<typename T> T *add(destruct_ptr<T> resource, const char *name)
|
||||
template<typename T> T *add(destruct_ptr<T> resource)
|
||||
{
|
||||
T *ptr = resource.release();
|
||||
if (ptr == nullptr) {
|
||||
@@ -104,13 +100,10 @@ class ResourceScope : NonCopyable, NonMovable {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
this->add(
|
||||
ptr,
|
||||
[](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
typed_data->~T();
|
||||
},
|
||||
name);
|
||||
this->add(ptr, [](void *data) {
|
||||
T *typed_data = reinterpret_cast<T *>(data);
|
||||
typed_data->~T();
|
||||
});
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -118,22 +111,31 @@ class ResourceScope : NonCopyable, NonMovable {
|
||||
* Pass ownership of some resource to the ResourceScope. The given free function will be
|
||||
* called when the collector is destructed.
|
||||
*/
|
||||
void add(void *userdata, void (*free)(void *), const char *name)
|
||||
void add(void *userdata, void (*free)(void *))
|
||||
{
|
||||
ResourceData data;
|
||||
data.debug_name = name;
|
||||
data.data = userdata;
|
||||
data.free = free;
|
||||
m_resources.append(data);
|
||||
resources_.append(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an object with the same value in the ResourceScope and return a reference to the
|
||||
* new value.
|
||||
*/
|
||||
template<typename T> T &add_value(T &&value, const char *name)
|
||||
template<typename T> T &add_value(T &&value)
|
||||
{
|
||||
return this->construct<T>(name, std::forward<T>(value));
|
||||
return this->construct<T>(std::forward<T>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* The passed in function will be called when the scope is destructed.
|
||||
*/
|
||||
template<typename Func> void add_destruct_call(Func func)
|
||||
{
|
||||
void *buffer = allocator_.allocate(sizeof(Func), alignof(Func));
|
||||
new (buffer) Func(std::move(func));
|
||||
this->add(buffer, [](void *data) { (*(Func *)data)(); });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,37 +144,19 @@ class ResourceScope : NonCopyable, NonMovable {
|
||||
*/
|
||||
LinearAllocator<> &linear_allocator()
|
||||
{
|
||||
return m_allocator;
|
||||
return allocator_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to construct an instance of type T that will be owned by the ResourceScope.
|
||||
*/
|
||||
template<typename T, typename... Args> T &construct(const char *name, Args &&...args)
|
||||
template<typename T, typename... Args> T &construct(Args &&...args)
|
||||
{
|
||||
destruct_ptr<T> value_ptr = m_allocator.construct<T>(std::forward<Args>(args)...);
|
||||
destruct_ptr<T> value_ptr = allocator_.construct<T>(std::forward<Args>(args)...);
|
||||
T &value_ref = *value_ptr;
|
||||
this->add(std::move(value_ptr), name);
|
||||
this->add(std::move(value_ptr));
|
||||
return value_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the names of all the resources that are owned by this ResourceScope. This can be
|
||||
* useful for debugging.
|
||||
*/
|
||||
void print(StringRef name) const
|
||||
{
|
||||
if (m_resources.size() == 0) {
|
||||
std::cout << "\"" << name << "\" has no resources.\n";
|
||||
return;
|
||||
}
|
||||
else {
|
||||
std::cout << "Resources for \"" << name << "\":\n";
|
||||
for (const ResourceData &data : m_resources) {
|
||||
std::cout << " " << data.data << ": " << data.debug_name << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace blender
|
||||
|
@@ -18,6 +18,13 @@
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Functions for generating and handling "Session UUIDs".
|
||||
*
|
||||
* Note that these are not true universally-unique identifiers, but only unique during the current
|
||||
* Blender session.
|
||||
*
|
||||
* For true UUIDs, see `BLI_uuid.h`.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -643,6 +643,16 @@ template<typename T> class MutableSpan {
|
||||
return MutableSpan(data_ + size_ - new_size, new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the data in the MutableSpan.
|
||||
*/
|
||||
constexpr void reverse()
|
||||
{
|
||||
for (const int i : IndexRange(size_ / 2)) {
|
||||
std::swap(data_[size_ - 1 - i], data_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an (immutable) Span that references the same array. This is usually not needed,
|
||||
* due to implicit conversions. However, sometimes automatic type deduction needs some help.
|
||||
|
67
source/blender/blenlib/BLI_uuid.h
Normal file
67
source/blender/blenlib/BLI_uuid.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*
|
||||
* Functions for generating and handling UUID structs according to RFC4122.
|
||||
*
|
||||
* Note that these are true UUIDs, not to be confused with the "session uuid" defined in
|
||||
* `BLI_session_uuid.h`.
|
||||
*/
|
||||
#include "DNA_uuid_types.h"
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* UUID generator for random (version 4) UUIDs. See RFC4122 section 4.4.
|
||||
* This function is not thread-safe. */
|
||||
UUID BLI_uuid_generate_random(void);
|
||||
|
||||
/** Compare two UUIDs, return true iff they are equal. */
|
||||
bool BLI_uuid_equal(UUID uuid1, UUID uuid2);
|
||||
|
||||
/**
|
||||
* Format UUID as string.
|
||||
* The buffer must be at least 37 bytes (36 bytes for the UUID + terminating 0).
|
||||
*/
|
||||
void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* Parse a string as UUID.
|
||||
* The string MUST be in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
|
||||
* as produced by #BLI_uuid_format().
|
||||
*
|
||||
* Return true if the string could be parsed, and false otherwise. In the latter case, the UUID may
|
||||
* have been partially updated.
|
||||
*/
|
||||
bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) ATTR_NONNULL();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
# include <ostream>
|
||||
|
||||
/** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */
|
||||
std::ostream &operator<<(std::ostream &stream, UUID uuid);
|
||||
|
||||
#endif
|
@@ -86,6 +86,7 @@ set(SRC
|
||||
intern/hash_md5.c
|
||||
intern/hash_mm2a.c
|
||||
intern/hash_mm3.c
|
||||
intern/index_mask.cc
|
||||
intern/jitter_2d.c
|
||||
intern/kdtree_1d.c
|
||||
intern/kdtree_2d.c
|
||||
@@ -146,6 +147,7 @@ set(SRC
|
||||
intern/time.c
|
||||
intern/timecode.c
|
||||
intern/timeit.cc
|
||||
intern/uuid.cc
|
||||
intern/uvproject.c
|
||||
intern/voronoi_2d.c
|
||||
intern/voxel.c
|
||||
@@ -309,6 +311,7 @@ set(SRC
|
||||
BLI_utildefines_stack.h
|
||||
BLI_utildefines_variadic.h
|
||||
BLI_utility_mixins.hh
|
||||
BLI_uuid.h
|
||||
BLI_uvproject.h
|
||||
BLI_vector.hh
|
||||
BLI_vector_adaptor.hh
|
||||
@@ -454,6 +457,7 @@ if(WITH_GTESTS)
|
||||
tests/BLI_string_utf8_test.cc
|
||||
tests/BLI_task_graph_test.cc
|
||||
tests/BLI_task_test.cc
|
||||
tests/BLI_uuid_test.cc
|
||||
tests/BLI_vector_set_test.cc
|
||||
tests/BLI_vector_test.cc
|
||||
tests/BLI_virtual_array_test.cc
|
||||
|
57
source/blender/blenlib/intern/index_mask.cc
Normal file
57
source/blender/blenlib/intern/index_mask.cc
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "BLI_index_mask.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/**
|
||||
* Create a sub-mask that is also shifted to the beginning. The shifting to the beginning allows
|
||||
* code to work with smaller indices, which is more memory efficient.
|
||||
*
|
||||
* \return New index mask with the size of #slice. It is either empty or starts with 0. It might
|
||||
* reference indices that have been appended to #r_new_indices.
|
||||
*
|
||||
* Example:
|
||||
* this: [2, 3, 5, 7, 8, 9, 10]
|
||||
* slice: ^--------^
|
||||
* output: [0, 2, 4, 5]
|
||||
*
|
||||
* All the indices in the sub-mask are shifted by 3 towards zero, so that the first index in the
|
||||
* output is zero.
|
||||
*/
|
||||
IndexMask IndexMask::slice_and_offset(const IndexRange slice, Vector<int64_t> &r_new_indices) const
|
||||
{
|
||||
const int slice_size = slice.size();
|
||||
if (slice_size == 0) {
|
||||
return {};
|
||||
}
|
||||
IndexMask sliced_mask{indices_.slice(slice)};
|
||||
if (sliced_mask.is_range()) {
|
||||
return IndexMask(slice_size);
|
||||
}
|
||||
const int64_t offset = sliced_mask.indices().first();
|
||||
if (offset == 0) {
|
||||
return sliced_mask;
|
||||
}
|
||||
r_new_indices.resize(slice_size);
|
||||
for (const int i : IndexRange(slice_size)) {
|
||||
r_new_indices[i] = sliced_mask[i] - offset;
|
||||
}
|
||||
return IndexMask(r_new_indices.as_span());
|
||||
}
|
||||
|
||||
} // namespace blender
|
116
source/blender/blenlib/intern/uuid.cc
Normal file
116
source/blender/blenlib/intern/uuid.cc
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup bli
|
||||
*/
|
||||
|
||||
#include "BLI_uuid.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <random>
|
||||
#include <string>
|
||||
|
||||
/* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */
|
||||
static_assert(sizeof(UUID) == 16, "expect UUIDs to be 128 bit exactly");
|
||||
|
||||
UUID BLI_uuid_generate_random()
|
||||
{
|
||||
static std::mt19937_64 rng = []() {
|
||||
std::mt19937_64 rng;
|
||||
|
||||
/* Ensure the RNG really can output 64-bit values. */
|
||||
static_assert(std::mt19937_64::min() == 0LL);
|
||||
static_assert(std::mt19937_64::max() == 0xffffffffffffffffLL);
|
||||
|
||||
struct timespec ts;
|
||||
timespec_get(&ts, TIME_UTC);
|
||||
rng.seed(ts.tv_nsec);
|
||||
|
||||
return rng;
|
||||
}();
|
||||
|
||||
UUID uuid;
|
||||
|
||||
/* RFC4122 suggests setting certain bits to a fixed value, and then randomizing the remaining
|
||||
* bits. The opposite is easier to implement, though, so that's what's done here. */
|
||||
|
||||
/* Read two 64-bit numbers to randomize all 128 bits of the UUID. */
|
||||
uint64_t *uuid_as_int64 = reinterpret_cast<uint64_t *>(&uuid);
|
||||
uuid_as_int64[0] = rng();
|
||||
uuid_as_int64[1] = rng();
|
||||
|
||||
/* Set the most significant four bits to 0b0100 to indicate version 4 (random UUID). */
|
||||
uuid.time_hi_and_version &= ~0xF000;
|
||||
uuid.time_hi_and_version |= 0x4000;
|
||||
|
||||
/* Set the most significant two bits to 0b10 to indicate compatibility with RFC4122. */
|
||||
uuid.clock_seq_hi_and_reserved &= ~0x40;
|
||||
uuid.clock_seq_hi_and_reserved |= 0x80;
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
bool BLI_uuid_equal(const UUID uuid1, const UUID uuid2)
|
||||
{
|
||||
return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0;
|
||||
}
|
||||
|
||||
void BLI_uuid_format(char *buffer, const UUID uuid)
|
||||
{
|
||||
std::sprintf(buffer,
|
||||
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
uuid.time_low,
|
||||
uuid.time_mid,
|
||||
uuid.time_hi_and_version,
|
||||
uuid.clock_seq_hi_and_reserved,
|
||||
uuid.clock_seq_low,
|
||||
uuid.node[0],
|
||||
uuid.node[1],
|
||||
uuid.node[2],
|
||||
uuid.node[3],
|
||||
uuid.node[4],
|
||||
uuid.node[5]);
|
||||
}
|
||||
|
||||
bool BLI_uuid_parse_string(UUID *uuid, const char *buffer)
|
||||
{
|
||||
const int num_fields_parsed = std::sscanf(
|
||||
buffer,
|
||||
"%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
|
||||
&uuid->time_low,
|
||||
&uuid->time_mid,
|
||||
&uuid->time_hi_and_version,
|
||||
&uuid->clock_seq_hi_and_reserved,
|
||||
&uuid->clock_seq_low,
|
||||
&uuid->node[0],
|
||||
&uuid->node[1],
|
||||
&uuid->node[2],
|
||||
&uuid->node[3],
|
||||
&uuid->node[4],
|
||||
&uuid->node[5]);
|
||||
return num_fields_parsed == 11;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, UUID uuid)
|
||||
{
|
||||
std::string buffer(36, '\0');
|
||||
BLI_uuid_format(buffer.data(), uuid);
|
||||
stream << buffer;
|
||||
return stream;
|
||||
}
|
@@ -128,6 +128,6 @@ TEST(color, SceneLinearByteDecoding)
|
||||
EXPECT_NEAR(0.5f, decoded.a, 0.01f);
|
||||
}
|
||||
|
||||
/* \} */
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::tests
|
||||
|
@@ -40,4 +40,28 @@ TEST(index_mask, RangeConstructor)
|
||||
EXPECT_EQ(indices[2], 5);
|
||||
}
|
||||
|
||||
TEST(index_mask, SliceAndOffset)
|
||||
{
|
||||
Vector<int64_t> indices;
|
||||
{
|
||||
IndexMask mask{IndexRange(10)};
|
||||
IndexMask new_mask = mask.slice_and_offset(IndexRange(3, 5), indices);
|
||||
EXPECT_TRUE(new_mask.is_range());
|
||||
EXPECT_EQ(new_mask.size(), 5);
|
||||
EXPECT_EQ(new_mask[0], 0);
|
||||
EXPECT_EQ(new_mask[1], 1);
|
||||
}
|
||||
{
|
||||
Vector<int64_t> original_indices = {2, 3, 5, 7, 8, 9, 10};
|
||||
IndexMask mask{original_indices.as_span()};
|
||||
IndexMask new_mask = mask.slice_and_offset(IndexRange(1, 4), indices);
|
||||
EXPECT_FALSE(new_mask.is_range());
|
||||
EXPECT_EQ(new_mask.size(), 4);
|
||||
EXPECT_EQ(new_mask[0], 0);
|
||||
EXPECT_EQ(new_mask[1], 2);
|
||||
EXPECT_EQ(new_mask[2], 4);
|
||||
EXPECT_EQ(new_mask[3], 5);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::tests
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user